@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.cjs CHANGED
@@ -348,8 +348,8 @@ var APIError = class extends Error {
348
348
  var k = /* @__PURE__ */ __name((key, store, personal) => ({ key, store, personal }), "k");
349
349
  var f = /* @__PURE__ */ __name((prefix, store, personal) => ({ prefix, store, personal }), "f");
350
350
  var CFG_KEYS = {
351
- accessToken: k("cfg.auth.access_token", "local", true),
352
- refreshToken: k("cfg.auth.refresh_token", "local", true),
351
+ accessToken: k("cfg.auth.access_token", "cookie", true),
352
+ refreshToken: k("cfg.auth.refresh_token", "cookie", true),
353
353
  apiKey: k("cfg.auth.api_key", "local", true),
354
354
  email: k("cfg.auth.email", "local", false),
355
355
  emailTs: k("cfg.auth.email_ts", "local", false),
@@ -384,6 +384,31 @@ function normalizeSegment(raw) {
384
384
  }
385
385
  __name(normalizeSegment, "normalizeSegment");
386
386
  var hasWindow = /* @__PURE__ */ __name(() => typeof window !== "undefined", "hasWindow");
387
+ var domainByHost = /* @__PURE__ */ new Map();
388
+ function detectCookieDomain() {
389
+ if (!hasWindow() || typeof document === "undefined") return null;
390
+ const host = window.location.hostname.toLowerCase();
391
+ if (!host || !host.includes(".") || host.includes(":") || /^[0-9.]+$/.test(host)) return null;
392
+ const cached = domainByHost.get(host);
393
+ if (cached !== void 0) return cached;
394
+ const labels = host.split(".");
395
+ const secure = window.location.protocol === "https:" ? "; Secure" : "";
396
+ let found = null;
397
+ try {
398
+ for (let i = labels.length - 2; i >= 0 && found === null; i -= 1) {
399
+ const candidate = labels.slice(i).join(".");
400
+ const probe = "cfg_probe_" + Math.random().toString(36).slice(2);
401
+ document.cookie = probe + "=1; Domain=" + candidate + "; Path=/; Max-Age=30; SameSite=Lax" + secure;
402
+ if (document.cookie.includes(probe + "=1")) found = candidate;
403
+ document.cookie = probe + "=; Domain=" + candidate + "; Path=/; Max-Age=0; SameSite=Lax" + secure;
404
+ }
405
+ } catch {
406
+ found = null;
407
+ }
408
+ domainByHost.set(host, found);
409
+ return found;
410
+ }
411
+ __name(detectCookieDomain, "detectCookieDomain");
387
412
  function nativeStorage(store) {
388
413
  if (!hasWindow()) return null;
389
414
  try {
@@ -409,6 +434,12 @@ function createStorage(owner, options = {}) {
409
434
  const prefix = `${owner}.`;
410
435
  const pattern = owner === "cfg" ? /^cfg(\.[a-z0-9_]+)+$/ : new RegExp(`^(cfg|${owner})(\\.[a-z0-9_]+)+$`);
411
436
  const cookieMaxAge = options.cookieMaxAgeSeconds ?? 60 * 60 * 24 * 30;
437
+ let cookiePolicy = options.cookieDomain ?? "auto";
438
+ const resolveCookieDomain = /* @__PURE__ */ __name(() => cookiePolicy === false ? null : cookiePolicy === "auto" ? detectCookieDomain() : cookiePolicy, "resolveCookieDomain");
439
+ const expireCookie = /* @__PURE__ */ __name((key, domain) => {
440
+ const secure = window.location.protocol === "https:" ? "; Secure" : "";
441
+ document.cookie = encodeURIComponent(key) + "=; Path=/; Max-Age=0; SameSite=Lax" + (domain ? "; Domain=" + domain : "") + secure;
442
+ }, "expireCookie");
412
443
  const specs = Object.values(options.keys ?? {});
413
444
  const families = Object.values(options.families ?? {});
414
445
  const specByKey = /* @__PURE__ */ new Map();
@@ -445,12 +476,15 @@ function createStorage(owner, options = {}) {
445
476
  return memory[store].get(key) ?? null;
446
477
  }
447
478
  __name(rawGet, "rawGet");
448
- function rawSet(store, key, value) {
479
+ function rawSet(store, key, value, maxAgeSeconds) {
449
480
  if (!hasWindow()) return false;
450
481
  try {
451
482
  if (store === "cookie") {
452
483
  const secure = window.location.protocol === "https:" ? "; Secure" : "";
453
- document.cookie = `${encodeURIComponent(key)}=${encodeURIComponent(value)}; Path=/; Max-Age=${cookieMaxAge}; SameSite=Lax${secure}`;
484
+ const domain = resolveCookieDomain();
485
+ const domainPart = domain ? `; Domain=${domain}` : "";
486
+ document.cookie = `${encodeURIComponent(key)}=${encodeURIComponent(value)}; Path=/; Max-Age=${Math.max(1, Math.floor(maxAgeSeconds ?? cookieMaxAge))}; SameSite=Lax${domainPart}${secure}`;
487
+ if (domain) expireCookie(key, null);
454
488
  if (rawGet("cookie", key) === value && !memory.cookie.has(key)) return true;
455
489
  memory.cookie.set(key, value);
456
490
  return false;
@@ -471,8 +505,9 @@ function createStorage(owner, options = {}) {
471
505
  if (!hasWindow()) return;
472
506
  try {
473
507
  if (store === "cookie") {
474
- const secure = window.location.protocol === "https:" ? "; Secure" : "";
475
- document.cookie = `${encodeURIComponent(key)}=; Path=/; Max-Age=0; SameSite=Lax${secure}`;
508
+ expireCookie(key, null);
509
+ const domain = resolveCookieDomain();
510
+ if (domain) expireCookie(key, domain);
476
511
  } else {
477
512
  nativeStorage(store)?.removeItem(key);
478
513
  }
@@ -521,10 +556,10 @@ function createStorage(owner, options = {}) {
521
556
  const target = resolveStore(key, store);
522
557
  return rawGet(target, key);
523
558
  },
524
- write(key, value, store) {
559
+ write(key, value, store, writeOptions) {
525
560
  assertOwned(key);
526
561
  const target = resolveStore(key, store);
527
- return rawSet(target, key, value);
562
+ return rawSet(target, key, value, writeOptions?.maxAgeSeconds);
528
563
  },
529
564
  remove(key, store) {
530
565
  assertOwned(key);
@@ -562,6 +597,10 @@ function createStorage(owner, options = {}) {
562
597
  for (const name of cookieNames()) if (name.startsWith(prefix)) rawRemove("cookie", name);
563
598
  }
564
599
  },
600
+ setCookieDomain(domain) {
601
+ cookiePolicy = domain;
602
+ },
603
+ cookieDomain: resolveCookieDomain,
565
604
  onChange(key, listener, store = "local") {
566
605
  assertValid(key);
567
606
  if (!hasWindow()) return () => {
@@ -588,6 +627,11 @@ var REFRESH_KEY = "cfg.auth.refresh_token";
588
627
  var API_KEY_KEY = "cfg.auth.api_key";
589
628
  var storage = true ? cfgStorage : createStorage2("cfg");
590
629
  var isBrowser = typeof window !== "undefined";
630
+ function secondsUntilExp(token) {
631
+ const exp = jwtExpMs(token);
632
+ return exp === null ? void 0 : Math.max(1, Math.floor((exp - Date.now()) / 1e3));
633
+ }
634
+ __name(secondsUntilExp, "secondsUntilExp");
591
635
  var STORE_OF = {
592
636
  localStorage: "local",
593
637
  sessionStorage: "session",
@@ -599,13 +643,31 @@ function backendFor(mode) {
599
643
  get: /* @__PURE__ */ __name((key) => storage.read(key, store), "get"),
600
644
  set(key, value) {
601
645
  if (value === null) storage.remove(key, store);
602
- else storage.write(key, value, store);
646
+ else storage.write(key, value, store, store === "cookie" && key === REFRESH_KEY ? { maxAgeSeconds: secondsUntilExp(value) } : void 0);
603
647
  }
604
648
  };
605
649
  }
606
650
  __name(backendFor, "backendFor");
607
- var _storageMode = "localStorage";
651
+ var _storageMode = "cookie";
608
652
  var _storage = backendFor(_storageMode);
653
+ var _apiKeyStore = backendFor("localStorage");
654
+ function adoptLegacySession() {
655
+ if (!isBrowser || _storageMode !== "cookie") return;
656
+ try {
657
+ if (_storage.get(ACCESS_KEY) !== null || _storage.get(REFRESH_KEY) !== null) return;
658
+ const legacy = backendFor("localStorage");
659
+ const access = legacy.get(ACCESS_KEY);
660
+ const refresh = legacy.get(REFRESH_KEY);
661
+ if (access === null && refresh === null) return;
662
+ if (access !== null) _storage.set(ACCESS_KEY, access);
663
+ if (refresh !== null) _storage.set(REFRESH_KEY, refresh);
664
+ legacy.set(ACCESS_KEY, null);
665
+ legacy.set(REFRESH_KEY, null);
666
+ } catch {
667
+ }
668
+ }
669
+ __name(adoptLegacySession, "adoptLegacySession");
670
+ adoptLegacySession();
609
671
  function detectLocale() {
610
672
  try {
611
673
  if (typeof document === "undefined") return null;
@@ -750,6 +812,14 @@ function ensureStorageSync() {
750
812
  }
751
813
  });
752
814
  window.addEventListener(SESSION_SYNC_EVENT, () => notifySessionChanged());
815
+ window.addEventListener("focus", () => notifySessionChanged());
816
+ document.addEventListener("visibilitychange", () => {
817
+ if (document.visibilityState === "visible") notifySessionChanged();
818
+ });
819
+ try {
820
+ window.cookieStore?.addEventListener("change", () => notifySessionChanged());
821
+ } catch {
822
+ }
753
823
  }
754
824
  __name(ensureStorageSync, "ensureStorageSync");
755
825
  var SESSION_EXPIRED_REGISTRY = `cfg-auth:expired-handlers:${ACCESS_KEY}`;
@@ -783,6 +853,17 @@ var auth = {
783
853
  _storage = backendFor(mode);
784
854
  notifySessionChanged();
785
855
  },
856
+ // ── Shared cookie domain ──────────────────────────────────────────
857
+ /** `'auto'` (default): the registrable domain of the page. A domain forces it; `null` keeps
858
+ * cookies host-only (no sign-in sharing between subdomains). Call before sign-in. */
859
+ setCookieDomain(domain) {
860
+ storage.setCookieDomain(domain === null ? false : domain);
861
+ notifySessionChanged();
862
+ },
863
+ /** The `Domain` the session cookie is written with; `null` = host-only. */
864
+ getCookieDomain() {
865
+ return storage.cookieDomain();
866
+ },
786
867
  // ── Bearer token ──────────────────────────────────────────────────
787
868
  getToken() {
788
869
  return _storage.get(ACCESS_KEY);
@@ -869,18 +950,18 @@ var auth = {
869
950
  },
870
951
  // ── API key ───────────────────────────────────────────────────────
871
952
  getApiKey() {
872
- return _apiKeyOverride ?? _storage.get(API_KEY_KEY) ?? defaultApiKey();
953
+ return _apiKeyOverride ?? _apiKeyStore.get(API_KEY_KEY) ?? defaultApiKey();
873
954
  },
874
955
  setApiKey(key) {
875
956
  _apiKeyOverride = key;
876
957
  },
877
958
  setApiKeyPersist(key) {
878
959
  _apiKeyOverride = key;
879
- _storage.set(API_KEY_KEY, key);
960
+ _apiKeyStore.set(API_KEY_KEY, key);
880
961
  },
881
962
  clearApiKey() {
882
963
  _apiKeyOverride = null;
883
- _storage.set(API_KEY_KEY, null);
964
+ _apiKeyStore.set(API_KEY_KEY, null);
884
965
  },
885
966
  // ── Locale ────────────────────────────────────────────────────────
886
967
  getLocale() {
@@ -4551,6 +4632,30 @@ var useDeleteAccount = /* @__PURE__ */ __name(() => {
4551
4632
  };
4552
4633
  }, "useDeleteAccount");
4553
4634
 
4635
+ // src/auth/hooks/legacyStorage.ts
4636
+ var LEGACY_TOKEN_KEYS = [
4637
+ "cfg.auth.access_token",
4638
+ "cfg.auth.refresh_token",
4639
+ "auth_token",
4640
+ "refresh_token",
4641
+ "access_token"
4642
+ ];
4643
+ function purgeLegacyTokenStorage() {
4644
+ const removed = [];
4645
+ try {
4646
+ if (typeof window === "undefined") return removed;
4647
+ for (const key of LEGACY_TOKEN_KEYS) {
4648
+ if (window.localStorage.getItem(key) !== null) {
4649
+ window.localStorage.removeItem(key);
4650
+ removed.push(key);
4651
+ }
4652
+ }
4653
+ } catch {
4654
+ }
4655
+ return removed;
4656
+ }
4657
+ __name(purgeLegacyTokenStorage, "purgeLegacyTokenStorage");
4658
+
4554
4659
  // src/auth/context/AccountsContext.tsx
4555
4660
  var import_react19 = require("react");
4556
4661
 
@@ -4915,6 +5020,10 @@ __name(hasApiOrigin, "hasApiOrigin");
4915
5020
  var AccountsContext = (0, import_react19.createContext)(void 0);
4916
5021
  function AccountsProvider({ children }) {
4917
5022
  const [profile, setProfile] = (0, import_react19.useState)(() => {
5023
+ if (!auth.isAuthenticated()) {
5024
+ clearProfileCache();
5025
+ return void 0;
5026
+ }
4918
5027
  const cached = getCachedProfile();
4919
5028
  return cached || void 0;
4920
5029
  });
@@ -5115,6 +5224,10 @@ var AuthProviderInternal = /* @__PURE__ */ __name(({ children, config }) => {
5115
5224
  authLogger.warn("Dead session in storage \u2014 clearing");
5116
5225
  auth.clearSession();
5117
5226
  clearProfileCache();
5227
+ } else if (!hasTokens && !auth.isAuthenticated()) {
5228
+ clearProfileCache();
5229
+ purgeLegacyTokenStorage();
5230
+ if (accountsRef.current.profile) accountsRef.current.logout();
5118
5231
  }
5119
5232
  setProfileSettled(true);
5120
5233
  return;