@djangocfg/api 2.1.333 → 2.1.334

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.
@@ -113,6 +113,9 @@ var _apiKeyOverride = null;
113
113
  var _baseUrlOverride = null;
114
114
  var _withCredentials = true;
115
115
  var _onUnauthorized = null;
116
+ var _refreshHandler = null;
117
+ var _refreshInflight = null;
118
+ var RETRY_MARKER = "X-Auth-Retry";
116
119
  var _client = null;
117
120
  function pushClientConfig() {
118
121
  if (!_client) return;
@@ -191,10 +194,53 @@ var auth = {
191
194
  pushClientConfig();
192
195
  },
193
196
  // ── 401 handler ───────────────────────────────────────────────────
197
+ /**
198
+ * Fired when the server returns 401 AND no refresh path recovers it
199
+ * (no refresh token, no refresh handler, refresh failed, or retry
200
+ * still 401). The app should clear local state and redirect to login.
201
+ *
202
+ * NOT fired for 401 that gets transparently recovered by the refresh
203
+ * handler — those are invisible to callers.
204
+ */
194
205
  onUnauthorized(cb) {
195
206
  _onUnauthorized = cb;
207
+ },
208
+ /**
209
+ * Register the refresh strategy. The handler receives the current
210
+ * refresh token and must call your refresh endpoint, returning
211
+ * `{ access, refresh? }` on success or `null` on failure.
212
+ *
213
+ * @example
214
+ * auth.setRefreshHandler(async (refresh) => {
215
+ * const { data } = await Auth.tokenRefreshCreate({ body: { refresh } });
216
+ * return data ? { access: data.access, refresh: data.refresh } : null;
217
+ * });
218
+ */
219
+ setRefreshHandler(fn) {
220
+ _refreshHandler = fn;
196
221
  }
197
222
  };
223
+ async function tryRefresh() {
224
+ if (_refreshInflight) return _refreshInflight;
225
+ if (!_refreshHandler) return null;
226
+ const refresh = auth.getRefreshToken();
227
+ if (!refresh) return null;
228
+ _refreshInflight = (async () => {
229
+ try {
230
+ const result = await _refreshHandler(refresh);
231
+ if (!result?.access) return null;
232
+ auth.setToken(result.access);
233
+ if (result.refresh) auth.setRefreshToken(result.refresh);
234
+ return result.access;
235
+ } catch {
236
+ return null;
237
+ } finally {
238
+ _refreshInflight = null;
239
+ }
240
+ })();
241
+ return _refreshInflight;
242
+ }
243
+ __name(tryRefresh, "tryRefresh");
198
244
  function installAuthOnClient(client2) {
199
245
  if (_client) return;
200
246
  _client = client2;
@@ -211,14 +257,48 @@ function installAuthOnClient(client2) {
211
257
  if (apiKey) request.headers.set("X-API-Key", apiKey);
212
258
  return request;
213
259
  });
214
- client2.interceptors.response.use((response) => {
215
- if (response.status === 401 && _onUnauthorized) {
216
- try {
217
- _onUnauthorized(response);
218
- } catch {
260
+ client2.interceptors.response.use(async (response, request) => {
261
+ if (response.status !== 401) return response;
262
+ if (request.headers.get(RETRY_MARKER)) {
263
+ if (_onUnauthorized) {
264
+ try {
265
+ _onUnauthorized(response);
266
+ } catch {
267
+ }
219
268
  }
269
+ return response;
270
+ }
271
+ const newToken = await tryRefresh();
272
+ if (!newToken) {
273
+ if (_onUnauthorized) {
274
+ try {
275
+ _onUnauthorized(response);
276
+ } catch {
277
+ }
278
+ }
279
+ return response;
280
+ }
281
+ const retry = request.clone();
282
+ retry.headers.set("Authorization", `Bearer ${newToken}`);
283
+ retry.headers.set(RETRY_MARKER, "1");
284
+ try {
285
+ const retried = await fetch(retry);
286
+ if (retried.status === 401 && _onUnauthorized) {
287
+ try {
288
+ _onUnauthorized(retried);
289
+ } catch {
290
+ }
291
+ }
292
+ return retried;
293
+ } catch {
294
+ if (_onUnauthorized) {
295
+ try {
296
+ _onUnauthorized(response);
297
+ } catch {
298
+ }
299
+ }
300
+ return response;
220
301
  }
221
- return response;
222
302
  });
223
303
  }
224
304
  __name(installAuthOnClient, "installAuthOnClient");
@@ -369,6 +449,15 @@ var API = class {
369
449
  setApiKey(key) {
370
450
  auth.setApiKey(key);
371
451
  }
452
+ // ── 401 handling ────────────────────────────────────────────────────────
453
+ /** Fired only on terminal 401 (after refresh+retry path is exhausted). */
454
+ onUnauthorized(cb) {
455
+ auth.onUnauthorized(cb);
456
+ }
457
+ /** Provide a refresh strategy. See `auth.setRefreshHandler` for the contract. */
458
+ setRefreshHandler(fn) {
459
+ auth.setRefreshHandler(fn);
460
+ }
372
461
  };
373
462
 
374
463
  // src/_api/generated/_cfg_centrifugo/api.ts
@@ -423,6 +512,15 @@ var API2 = class {
423
512
  setApiKey(key) {
424
513
  auth.setApiKey(key);
425
514
  }
515
+ // ── 401 handling ────────────────────────────────────────────────────────
516
+ /** Fired only on terminal 401 (after refresh+retry path is exhausted). */
517
+ onUnauthorized(cb) {
518
+ auth.onUnauthorized(cb);
519
+ }
520
+ /** Provide a refresh strategy. See `auth.setRefreshHandler` for the contract. */
521
+ setRefreshHandler(fn) {
522
+ auth.setRefreshHandler(fn);
523
+ }
426
524
  };
427
525
 
428
526
  // src/_api/generated/_cfg_totp/api.ts
@@ -477,6 +575,15 @@ var API3 = class {
477
575
  setApiKey(key) {
478
576
  auth.setApiKey(key);
479
577
  }
578
+ // ── 401 handling ────────────────────────────────────────────────────────
579
+ /** Fired only on terminal 401 (after refresh+retry path is exhausted). */
580
+ onUnauthorized(cb) {
581
+ auth.onUnauthorized(cb);
582
+ }
583
+ /** Provide a refresh strategy. See `auth.setRefreshHandler` for the contract. */
584
+ setRefreshHandler(fn) {
585
+ auth.setRefreshHandler(fn);
586
+ }
480
587
  };
481
588
 
482
589
  // src/_api/generated/index.ts