@djangocfg/api 2.1.333 → 2.1.335

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.
@@ -142,6 +142,9 @@ var _apiKeyOverride = null;
142
142
  var _baseUrlOverride = null;
143
143
  var _withCredentials = true;
144
144
  var _onUnauthorized = null;
145
+ var _refreshHandler = null;
146
+ var _refreshInflight = null;
147
+ var RETRY_MARKER = "X-Auth-Retry";
145
148
  var _client = null;
146
149
  function pushClientConfig() {
147
150
  if (!_client) return;
@@ -220,10 +223,53 @@ var auth = {
220
223
  pushClientConfig();
221
224
  },
222
225
  // ── 401 handler ───────────────────────────────────────────────────
226
+ /**
227
+ * Fired when the server returns 401 AND no refresh path recovers it
228
+ * (no refresh token, no refresh handler, refresh failed, or retry
229
+ * still 401). The app should clear local state and redirect to login.
230
+ *
231
+ * NOT fired for 401 that gets transparently recovered by the refresh
232
+ * handler — those are invisible to callers.
233
+ */
223
234
  onUnauthorized(cb) {
224
235
  _onUnauthorized = cb;
236
+ },
237
+ /**
238
+ * Register the refresh strategy. The handler receives the current
239
+ * refresh token and must call your refresh endpoint, returning
240
+ * `{ access, refresh? }` on success or `null` on failure.
241
+ *
242
+ * @example
243
+ * auth.setRefreshHandler(async (refresh) => {
244
+ * const { data } = await Auth.tokenRefreshCreate({ body: { refresh } });
245
+ * return data ? { access: data.access, refresh: data.refresh } : null;
246
+ * });
247
+ */
248
+ setRefreshHandler(fn) {
249
+ _refreshHandler = fn;
225
250
  }
226
251
  };
252
+ async function tryRefresh() {
253
+ if (_refreshInflight) return _refreshInflight;
254
+ if (!_refreshHandler) return null;
255
+ const refresh = auth.getRefreshToken();
256
+ if (!refresh) return null;
257
+ _refreshInflight = (async () => {
258
+ try {
259
+ const result = await _refreshHandler(refresh);
260
+ if (!result?.access) return null;
261
+ auth.setToken(result.access);
262
+ if (result.refresh) auth.setRefreshToken(result.refresh);
263
+ return result.access;
264
+ } catch {
265
+ return null;
266
+ } finally {
267
+ _refreshInflight = null;
268
+ }
269
+ })();
270
+ return _refreshInflight;
271
+ }
272
+ __name(tryRefresh, "tryRefresh");
227
273
  function installAuthOnClient(client2) {
228
274
  if (_client) return;
229
275
  _client = client2;
@@ -240,14 +286,48 @@ function installAuthOnClient(client2) {
240
286
  if (apiKey) request.headers.set("X-API-Key", apiKey);
241
287
  return request;
242
288
  });
243
- client2.interceptors.response.use((response) => {
244
- if (response.status === 401 && _onUnauthorized) {
245
- try {
246
- _onUnauthorized(response);
247
- } catch {
289
+ client2.interceptors.response.use(async (response, request) => {
290
+ if (response.status !== 401) return response;
291
+ if (request.headers.get(RETRY_MARKER)) {
292
+ if (_onUnauthorized) {
293
+ try {
294
+ _onUnauthorized(response);
295
+ } catch {
296
+ }
248
297
  }
298
+ return response;
299
+ }
300
+ const newToken = await tryRefresh();
301
+ if (!newToken) {
302
+ if (_onUnauthorized) {
303
+ try {
304
+ _onUnauthorized(response);
305
+ } catch {
306
+ }
307
+ }
308
+ return response;
309
+ }
310
+ const retry = request.clone();
311
+ retry.headers.set("Authorization", `Bearer ${newToken}`);
312
+ retry.headers.set(RETRY_MARKER, "1");
313
+ try {
314
+ const retried = await fetch(retry);
315
+ if (retried.status === 401 && _onUnauthorized) {
316
+ try {
317
+ _onUnauthorized(retried);
318
+ } catch {
319
+ }
320
+ }
321
+ return retried;
322
+ } catch {
323
+ if (_onUnauthorized) {
324
+ try {
325
+ _onUnauthorized(response);
326
+ } catch {
327
+ }
328
+ }
329
+ return response;
249
330
  }
250
- return response;
251
331
  });
252
332
  }
253
333
  __name(installAuthOnClient, "installAuthOnClient");
@@ -398,6 +478,15 @@ var API = class {
398
478
  setApiKey(key) {
399
479
  auth.setApiKey(key);
400
480
  }
481
+ // ── 401 handling ────────────────────────────────────────────────────────
482
+ /** Fired only on terminal 401 (after refresh+retry path is exhausted). */
483
+ onUnauthorized(cb) {
484
+ auth.onUnauthorized(cb);
485
+ }
486
+ /** Provide a refresh strategy. See `auth.setRefreshHandler` for the contract. */
487
+ setRefreshHandler(fn) {
488
+ auth.setRefreshHandler(fn);
489
+ }
401
490
  };
402
491
 
403
492
  // src/_api/generated/_cfg_centrifugo/api.ts
@@ -452,6 +541,15 @@ var API2 = class {
452
541
  setApiKey(key) {
453
542
  auth.setApiKey(key);
454
543
  }
544
+ // ── 401 handling ────────────────────────────────────────────────────────
545
+ /** Fired only on terminal 401 (after refresh+retry path is exhausted). */
546
+ onUnauthorized(cb) {
547
+ auth.onUnauthorized(cb);
548
+ }
549
+ /** Provide a refresh strategy. See `auth.setRefreshHandler` for the contract. */
550
+ setRefreshHandler(fn) {
551
+ auth.setRefreshHandler(fn);
552
+ }
455
553
  };
456
554
 
457
555
  // src/_api/generated/_cfg_totp/api.ts
@@ -506,6 +604,15 @@ var API3 = class {
506
604
  setApiKey(key) {
507
605
  auth.setApiKey(key);
508
606
  }
607
+ // ── 401 handling ────────────────────────────────────────────────────────
608
+ /** Fired only on terminal 401 (after refresh+retry path is exhausted). */
609
+ onUnauthorized(cb) {
610
+ auth.onUnauthorized(cb);
611
+ }
612
+ /** Provide a refresh strategy. See `auth.setRefreshHandler` for the contract. */
613
+ setRefreshHandler(fn) {
614
+ auth.setRefreshHandler(fn);
615
+ }
509
616
  };
510
617
 
511
618
  // src/_api/generated/index.ts