@djangocfg/api 2.1.486 → 2.1.488

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@djangocfg/api",
3
- "version": "2.1.486",
3
+ "version": "2.1.488",
4
4
  "description": "Auto-generated TypeScript API client with React hooks, SWR integration, and Zod validation for Django REST Framework backends",
5
5
  "keywords": [
6
6
  "django",
@@ -84,7 +84,7 @@
84
84
  "zod": "^4.3.6"
85
85
  },
86
86
  "devDependencies": {
87
- "@djangocfg/typescript-config": "^2.1.486",
87
+ "@djangocfg/typescript-config": "^2.1.488",
88
88
  "@testing-library/react": "^16.3.2",
89
89
  "@types/node": "^25.9.5",
90
90
  "@types/react": "19.2.15",
@@ -86,31 +86,20 @@ function backendFor(mode: StorageMode): KVStore {
86
86
  }
87
87
 
88
88
  /**
89
- * Which backend holds the session persisted, because the mode has to outlive
90
- * the module.
89
+ * The session backend. `localStorage` by default and for every login path.
91
90
  *
92
- * A non-persistent sign-in ("remember me" unchecked) writes its tokens to
93
- * `sessionStorage`. Without this marker the next page load would start from the
94
- * `localStorage` default and look there instead finding nothing, and silently
95
- * bouncing an authenticated user back to the login form. The marker itself is
96
- * NOT the session: it lives in `localStorage` (a plain enum, no credential), so
97
- * only the tokens follow the tab's lifetime.
91
+ * `setStorageMode` exists for a host that must relocate the whole session once
92
+ * at bootstrap e.g. 'cookie' so Next.js `cookies()` can read the token during
93
+ * SSR. It is NOT a per-login switch: this value lives in module memory and
94
+ * resets to the default on every page load, so tokens written to a non-default
95
+ * backend after startup would be stranded present in the browser, invisible
96
+ * to the client, and every request would go out unauthenticated.
97
+ *
98
+ * Session LENGTH is therefore not a client concern. The server decides it when
99
+ * minting the refresh token (`remember_me`); the client just stores whatever it
100
+ * was given, in one place.
98
101
  */
99
- const STORAGE_MODE_KEY = 'cfg.access_token.storage_mode';
100
-
101
- function readPersistedMode(): StorageMode {
102
- if (!isBrowser) return 'localStorage';
103
- try {
104
- const raw = window.localStorage.getItem(STORAGE_MODE_KEY);
105
- return raw === 'sessionStorage' || raw === 'cookie' || raw === 'localStorage'
106
- ? raw
107
- : 'localStorage';
108
- } catch {
109
- return 'localStorage';
110
- }
111
- }
112
-
113
- let _storageMode: StorageMode = readPersistedMode();
102
+ let _storageMode: StorageMode = 'localStorage';
114
103
  let _storage: KVStore = backendFor(_storageMode);
115
104
 
116
105
  /** Detect locale from `NEXT_LOCALE` cookie or `navigator.language`. */
@@ -357,13 +346,12 @@ function pushClientConfig(): void {
357
346
  export const auth = {
358
347
  // ── Storage mode ──────────────────────────────────────────────────
359
348
  getStorageMode(): StorageMode { return _storageMode; },
349
+ /** Relocate the session backend. Call once at bootstrap, before any sign-in:
350
+ * the mode is module state and resets on reload, so a later switch strands
351
+ * whatever was written after it. */
360
352
  setStorageMode(mode: StorageMode): void {
361
353
  _storageMode = mode;
362
354
  _storage = backendFor(mode);
363
- // Persist the choice so the next page load reads from the same backend.
364
- if (isBrowser) {
365
- try { window.localStorage.setItem(STORAGE_MODE_KEY, mode); } catch {}
366
- }
367
355
  notifySessionChanged();
368
356
  },
369
357
 
@@ -379,19 +367,13 @@ export const auth = {
379
367
  notifySessionChanged();
380
368
  },
381
369
  clearTokens(): void {
382
- // Clear every backend, not just the active one: a sign-out must not leave a
383
- // stale token behind in the backend the PREVIOUS session used, or the next
384
- // page load would resurrect it.
370
+ // Clear every backend, not just the active one. Sign-out must leave nothing
371
+ // behind anywhere including tokens an older build stranded in
372
+ // sessionStorage — or the next page load could resurrect a dead session.
385
373
  for (const backend of [localStorageBackend, sessionStorageBackend, cookieBackend]) {
386
374
  backend.set(ACCESS_KEY, null);
387
375
  backend.set(REFRESH_KEY, null);
388
376
  }
389
- // Back to the default, so the next sign-in starts from a known mode.
390
- _storageMode = 'localStorage';
391
- _storage = localStorageBackend;
392
- if (isBrowser) {
393
- try { window.localStorage.removeItem(STORAGE_MODE_KEY); } catch {}
394
- }
395
377
  notifySessionChanged();
396
378
  },
397
379
  /** Session-aware: token PRESENT and not past its `exp` (or a live refresh
@@ -183,7 +183,11 @@ export function AccountsProvider({ children }: AccountsProviderProps) {
183
183
 
184
184
  if (result.access && result.refresh) {
185
185
  // The ONE write path — atomic pair, notifies every session subscriber.
186
- auth.setStorageMode(result.persistent_session ? 'localStorage' : 'sessionStorage');
186
+ // Storage is always localStorage: "remember me" is a TOKEN LIFETIME
187
+ // decision the server already made (`remember_me` → REMEMBER_ME_LIFETIME),
188
+ // not a storage decision. Switching the client to sessionStorage here used
189
+ // to strand the tokens — the module reinitializes to localStorage on the
190
+ // next page load and would never look at sessionStorage again.
187
191
  auth.setSession({ access: result.access, refresh: result.refresh });
188
192
  try {
189
193
  await refreshProfile({ callerId: 'verifyOTP', force: true });
@@ -168,8 +168,9 @@ export const useGithubAuth = (options: UseGithubAuthOptions = {}): UseGithubAuth
168
168
 
169
169
  authLogger.info('GitHub OAuth successful, user:', response.user);
170
170
 
171
- auth.setStorageMode(response.persistent_session ? 'localStorage' : 'sessionStorage');
172
- // The ONE write path atomic pair + session notification.
171
+ // The ONE write path — atomic pair + session notification. Storage is
172
+ // always localStorage; session length is the server's call via
173
+ // `remember_me`. See AccountsContext.verifyOTP.
173
174
  auth.setSession({ access: response.access, refresh: response.refresh });
174
175
 
175
176
  // Track successful OAuth
@@ -71,8 +71,9 @@ export const useTwoFactorVerify = (
71
71
  remaining_backup_codes?: number;
72
72
  persistent_session: boolean;
73
73
  }) => {
74
- auth.setStorageMode(response.persistent_session ? 'localStorage' : 'sessionStorage');
75
74
  // Save tokens — the ONE write path (atomic pair + session notification).
75
+ // Storage is always localStorage; session length is the server's call via
76
+ // `remember_me`. See AccountsContext.verifyOTP.
76
77
  auth.setSession({ access: response.access_token, refresh: response.refresh_token });
77
78
 
78
79
  if (response.warning) {