@nauth-toolkit/client-angular 0.1.64 → 0.1.65

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.
@@ -2,7 +2,7 @@ import { NAuthErrorCode, NAuthClientError, NAuthClient } from '@nauth-toolkit/cl
2
2
  export * from '@nauth-toolkit/client';
3
3
  import * as i0 from '@angular/core';
4
4
  import { InjectionToken, Injectable, Inject, inject, Optional, NgModule, PLATFORM_ID } from '@angular/core';
5
- import { firstValueFrom, BehaviorSubject, Subject, catchError, from, switchMap, throwError, filter as filter$1, take } from 'rxjs';
5
+ import { firstValueFrom, BehaviorSubject, Subject, from, map, of, switchMap, catchError, throwError, filter as filter$1, take } from 'rxjs';
6
6
  import { filter } from 'rxjs/operators';
7
7
  import * as i1 from '@angular/common/http';
8
8
  import { HttpErrorResponse, HTTP_INTERCEPTORS, HttpClientModule, HttpClient } from '@angular/common/http';
@@ -604,6 +604,22 @@ class AuthService {
604
604
  await this.client.clearStoredChallenge();
605
605
  this.challengeSubject.next(null);
606
606
  }
607
+ /**
608
+ * Get current access token (JSON mode only).
609
+ *
610
+ * This is primarily useful for consumers using Angular `HttpClient` directly
611
+ * (outside of the SDK methods) and relying on an interceptor to attach Bearer tokens.
612
+ *
613
+ * @returns Access token, or null if not available
614
+ *
615
+ * @example
616
+ * ```typescript
617
+ * const token = await this.auth.getAccessToken();
618
+ * ```
619
+ */
620
+ async getAccessToken() {
621
+ return await this.client.getAccessToken();
622
+ }
607
623
  // ============================================================================
608
624
  // Social Authentication
609
625
  // ============================================================================
@@ -946,20 +962,139 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
946
962
  }] }, { type: AngularHttpAdapter }] });
947
963
 
948
964
  /**
949
- * Refresh state management.
965
+ * Shared interceptor logic for both:
966
+ * - Functional interceptor (Angular 17+ standalone)
967
+ * - Class-based interceptor (NgModule apps)
968
+ *
969
+ * WHY:
970
+ * - Keep one implementation for cookies + json mode behavior.
971
+ * - Avoid divergence between standalone and NgModule integrations.
950
972
  */
951
- let isRefreshing$1 = false;
952
- const refreshTokenSubject$1 = new BehaviorSubject(null);
953
- const retriedRequests$1 = new WeakSet();
973
+ // ============================================================================
974
+ // Refresh state management (module-level)
975
+ // ============================================================================
976
+ let isRefreshing = false;
977
+ const refreshTokenSubject = new BehaviorSubject(null);
978
+ const retriedRequests = new WeakSet();
954
979
  /**
955
980
  * Get CSRF token from cookie.
956
981
  */
957
- function getCsrfToken$1(cookieName) {
982
+ function getCsrfToken(cookieName) {
958
983
  if (typeof document === 'undefined')
959
984
  return null;
960
985
  const match = document.cookie.match(new RegExp(`(^| )${cookieName}=([^;]+)`));
961
986
  return match ? decodeURIComponent(match[2]) : null;
962
987
  }
988
+ /**
989
+ * Build retry request with appropriate auth.
990
+ *
991
+ * In cookies mode: Browser automatically sends updated httpOnly cookies (access/refresh tokens).
992
+ * We must re-read CSRF token after refresh to avoid stale headers.
993
+ *
994
+ * In JSON mode: Clones the request and adds the new Bearer token.
995
+ */
996
+ function buildRetryRequest(originalReq, tokenDelivery, newToken, csrfConfig) {
997
+ if (tokenDelivery === 'json' && newToken && newToken !== 'success') {
998
+ return originalReq.clone({ setHeaders: { Authorization: `Bearer ${newToken}` } });
999
+ }
1000
+ if (tokenDelivery === 'cookies' && ['POST', 'PUT', 'PATCH', 'DELETE'].includes(originalReq.method)) {
1001
+ const csrfCookieName = csrfConfig?.cookieName ?? 'nauth_csrf_token';
1002
+ const csrfHeaderName = csrfConfig?.headerName ?? 'x-csrf-token';
1003
+ const freshCsrfToken = getCsrfToken(csrfCookieName);
1004
+ if (freshCsrfToken) {
1005
+ return originalReq.clone({ setHeaders: { [csrfHeaderName]: freshCsrfToken } });
1006
+ }
1007
+ }
1008
+ return originalReq;
1009
+ }
1010
+ function createNAuthAuthHttpInterceptor(params) {
1011
+ const { config, http, authService, router, next, req } = params;
1012
+ const tokenDelivery = config.tokenDelivery;
1013
+ const baseUrl = config.baseUrl;
1014
+ const endpoints = config.endpoints ?? {};
1015
+ const refreshPath = endpoints.refresh ?? '/refresh';
1016
+ const loginPath = endpoints.login ?? '/login';
1017
+ const signupPath = endpoints.signup ?? '/signup';
1018
+ const socialExchangePath = endpoints.socialExchange ?? '/social/exchange';
1019
+ const refreshUrl = `${baseUrl}${refreshPath}`;
1020
+ const isAuthApiRequest = req.url.includes(baseUrl);
1021
+ const isRefreshEndpoint = req.url.includes(refreshPath);
1022
+ const isPublicEndpoint = req.url.includes(loginPath) || req.url.includes(signupPath) || req.url.includes(socialExchangePath);
1023
+ // ============================================================================
1024
+ // Build request for cookies mode (withCredentials + CSRF)
1025
+ // ============================================================================
1026
+ let authReq = req;
1027
+ if (tokenDelivery === 'cookies') {
1028
+ authReq = authReq.clone({ withCredentials: true });
1029
+ if (['POST', 'PUT', 'PATCH', 'DELETE'].includes(req.method)) {
1030
+ const csrfCookieName = config.csrf?.cookieName ?? 'nauth_csrf_token';
1031
+ const csrfHeaderName = config.csrf?.headerName ?? 'x-csrf-token';
1032
+ const csrfToken = getCsrfToken(csrfCookieName);
1033
+ if (csrfToken) {
1034
+ authReq = authReq.clone({ setHeaders: { [csrfHeaderName]: csrfToken } });
1035
+ }
1036
+ }
1037
+ }
1038
+ // ============================================================================
1039
+ // JSON mode: attach Authorization header for HttpClient calls
1040
+ // ============================================================================
1041
+ const attachJsonAuth$ = tokenDelivery === 'json' &&
1042
+ isAuthApiRequest &&
1043
+ !isRefreshEndpoint &&
1044
+ !isPublicEndpoint &&
1045
+ !authReq.headers.has('Authorization')
1046
+ ? from(authService.getAccessToken()).pipe(map((token) => {
1047
+ if (!token)
1048
+ return authReq;
1049
+ return authReq.clone({ setHeaders: { Authorization: `Bearer ${token}` } });
1050
+ }))
1051
+ : of(authReq);
1052
+ return attachJsonAuth$.pipe(switchMap((requestWithAuth) => next(requestWithAuth).pipe(catchError((error) => {
1053
+ const shouldHandle = error instanceof HttpErrorResponse &&
1054
+ error.status === 401 &&
1055
+ isAuthApiRequest &&
1056
+ !isRefreshEndpoint &&
1057
+ !isPublicEndpoint &&
1058
+ !retriedRequests.has(req);
1059
+ if (!shouldHandle) {
1060
+ return throwError(() => error);
1061
+ }
1062
+ retriedRequests.add(req);
1063
+ if (!isRefreshing) {
1064
+ isRefreshing = true;
1065
+ refreshTokenSubject.next(null);
1066
+ // Refresh based on mode
1067
+ const refresh$ = tokenDelivery === 'cookies'
1068
+ ? http.post(refreshUrl, {}, { withCredentials: true })
1069
+ : from(authService.refresh());
1070
+ return refresh$.pipe(switchMap((response) => {
1071
+ isRefreshing = false;
1072
+ const newToken = 'accessToken' in response ? response.accessToken : 'success';
1073
+ refreshTokenSubject.next(newToken ?? 'success');
1074
+ const retryReq = buildRetryRequest(requestWithAuth, tokenDelivery, newToken, config.csrf);
1075
+ return next(retryReq).pipe(catchError((retryErr) => {
1076
+ return throwError(() => retryErr);
1077
+ }));
1078
+ }), catchError((err) => {
1079
+ isRefreshing = false;
1080
+ refreshTokenSubject.next(null);
1081
+ // Refresh failed -> redirect if configured
1082
+ if (config.redirects?.sessionExpired) {
1083
+ router.navigateByUrl(config.redirects.sessionExpired).catch(() => { });
1084
+ }
1085
+ return throwError(() => err);
1086
+ }));
1087
+ }
1088
+ // Wait for ongoing refresh
1089
+ return refreshTokenSubject.pipe(filter$1((token) => token !== null), take(1), switchMap((token) => {
1090
+ const retryReq = buildRetryRequest(requestWithAuth, tokenDelivery, token, config.csrf);
1091
+ return next(retryReq).pipe(catchError((retryErr) => {
1092
+ return throwError(() => retryErr);
1093
+ }));
1094
+ }));
1095
+ }))));
1096
+ }
1097
+
963
1098
  /**
964
1099
  * Class-based HTTP interceptor for NgModule apps (Angular < 17).
965
1100
  *
@@ -990,52 +1125,14 @@ class AuthInterceptorClass {
990
1125
  this.router = router;
991
1126
  }
992
1127
  intercept(req, next) {
993
- const tokenDelivery = this.config.tokenDelivery;
994
- const baseUrl = this.config.baseUrl;
995
- // ============================================================================
996
- // COOKIES MODE: withCredentials + CSRF token
997
- // ============================================================================
998
- if (tokenDelivery === 'cookies') {
999
- let clonedReq = req.clone({ withCredentials: true });
1000
- // Add CSRF token header if it's a mutating request
1001
- if (['POST', 'PUT', 'PATCH', 'DELETE'].includes(req.method)) {
1002
- const csrfToken = getCsrfToken$1(this.config.csrf?.cookieName || 'XSRF-TOKEN');
1003
- if (csrfToken) {
1004
- clonedReq = clonedReq.clone({
1005
- setHeaders: { [this.config.csrf?.headerName || 'X-XSRF-TOKEN']: csrfToken },
1006
- });
1007
- }
1008
- }
1009
- return next.handle(clonedReq).pipe(catchError((error) => {
1010
- if (error.status === 401 && !retriedRequests$1.has(req)) {
1011
- retriedRequests$1.add(req);
1012
- if (!isRefreshing$1) {
1013
- isRefreshing$1 = true;
1014
- refreshTokenSubject$1.next(null);
1015
- return from(this.http
1016
- .post(`${baseUrl}/refresh`, {}, { withCredentials: true })
1017
- .toPromise()).pipe(switchMap(() => {
1018
- isRefreshing$1 = false;
1019
- refreshTokenSubject$1.next('refreshed');
1020
- return next.handle(clonedReq);
1021
- }), catchError((refreshError) => {
1022
- isRefreshing$1 = false;
1023
- this.authService.logout();
1024
- this.router.navigate([this.config.redirects?.sessionExpired || '/login']);
1025
- return throwError(() => refreshError);
1026
- }));
1027
- }
1028
- else {
1029
- return refreshTokenSubject$1.pipe(filter$1((token) => token !== null), take(1), switchMap(() => next.handle(clonedReq)));
1030
- }
1031
- }
1032
- return throwError(() => error);
1033
- }));
1034
- }
1035
- // ============================================================================
1036
- // JSON MODE: Delegate to SDK for token handling
1037
- // ============================================================================
1038
- return next.handle(req);
1128
+ return createNAuthAuthHttpInterceptor({
1129
+ config: this.config,
1130
+ http: this.http,
1131
+ authService: this.authService,
1132
+ router: this.router,
1133
+ req,
1134
+ next: (r) => next.handle(r),
1135
+ });
1039
1136
  }
1040
1137
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: AuthInterceptorClass, deps: [{ token: NAUTH_CLIENT_CONFIG }, { token: i1.HttpClient }, { token: AuthService }, { token: i3.Router }], target: i0.ɵɵFactoryTarget.Injectable });
1041
1138
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: AuthInterceptorClass });
@@ -1225,25 +1322,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
1225
1322
  }]
1226
1323
  }] });
1227
1324
 
1228
- /**
1229
- * Refresh state management.
1230
- * BehaviorSubject pattern is the industry-standard for token refresh.
1231
- */
1232
- let isRefreshing = false;
1233
- const refreshTokenSubject = new BehaviorSubject(null);
1234
- /**
1235
- * Track retried requests to prevent infinite loops.
1236
- */
1237
- const retriedRequests = new WeakSet();
1238
- /**
1239
- * Get CSRF token from cookie.
1240
- */
1241
- function getCsrfToken(cookieName) {
1242
- if (typeof document === 'undefined')
1243
- return null;
1244
- const match = document.cookie.match(new RegExp(`(^| )${cookieName}=([^;]+)`));
1245
- return match ? decodeURIComponent(match[2]) : null;
1246
- }
1247
1325
  /**
1248
1326
  * Angular HTTP interceptor for nauth-toolkit.
1249
1327
  *
@@ -1261,248 +1339,8 @@ const authInterceptor = (req, next) => {
1261
1339
  if (!isBrowser) {
1262
1340
  return next(req);
1263
1341
  }
1264
- // #region agent log
1265
- if (req.url.includes('/profile') && req.method === 'PUT') {
1266
- fetch('http://127.0.0.1:7242/ingest/97f9fe53-6a8b-43e2-ae9b-4b2d0f725816', {
1267
- method: 'POST',
1268
- headers: { 'Content-Type': 'application/json' },
1269
- body: JSON.stringify({
1270
- location: 'auth.interceptor.ts:entry',
1271
- message: 'Original request entry',
1272
- data: { reqBody: req.body, reqBodyType: typeof req.body, reqMethod: req.method, reqUrl: req.url },
1273
- timestamp: Date.now(),
1274
- sessionId: 'debug-session',
1275
- hypothesisId: 'A',
1276
- }),
1277
- }).catch(() => { });
1278
- }
1279
- // #endregion
1280
- const tokenDelivery = config.tokenDelivery;
1281
- const baseUrl = config.baseUrl;
1282
- const endpoints = config.endpoints ?? {};
1283
- const refreshPath = endpoints.refresh ?? '/refresh';
1284
- const loginPath = endpoints.login ?? '/login';
1285
- const signupPath = endpoints.signup ?? '/signup';
1286
- const socialExchangePath = endpoints.socialExchange ?? '/social/exchange';
1287
- const refreshUrl = `${baseUrl}${refreshPath}`;
1288
- const isAuthApiRequest = req.url.includes(baseUrl);
1289
- const isRefreshEndpoint = req.url.includes(refreshPath);
1290
- const isPublicEndpoint = req.url.includes(loginPath) || req.url.includes(signupPath) || req.url.includes(socialExchangePath);
1291
- // Build request with credentials (cookies mode only)
1292
- let authReq = req;
1293
- if (tokenDelivery === 'cookies') {
1294
- authReq = authReq.clone({ withCredentials: true });
1295
- if (['POST', 'PUT', 'PATCH', 'DELETE'].includes(req.method)) {
1296
- const csrfCookieName = config.csrf?.cookieName ?? 'nauth_csrf_token';
1297
- const csrfHeaderName = config.csrf?.headerName ?? 'x-csrf-token';
1298
- const csrfToken = getCsrfToken(csrfCookieName);
1299
- if (csrfToken) {
1300
- authReq = authReq.clone({ setHeaders: { [csrfHeaderName]: csrfToken } });
1301
- }
1302
- }
1303
- }
1304
- return next(authReq).pipe(catchError((error) => {
1305
- const shouldHandle = error instanceof HttpErrorResponse &&
1306
- error.status === 401 &&
1307
- isAuthApiRequest &&
1308
- !isRefreshEndpoint &&
1309
- !isPublicEndpoint &&
1310
- !retriedRequests.has(req);
1311
- if (!shouldHandle) {
1312
- return throwError(() => error);
1313
- }
1314
- // Mark original request as retried to prevent infinite loops
1315
- retriedRequests.add(req);
1316
- if (config.debug) {
1317
- console.warn('[nauth-interceptor] 401 detected:', req.url);
1318
- }
1319
- if (!isRefreshing) {
1320
- isRefreshing = true;
1321
- refreshTokenSubject.next(null);
1322
- if (config.debug) {
1323
- console.warn('[nauth-interceptor] Starting refresh...');
1324
- }
1325
- // Refresh based on mode
1326
- const refresh$ = tokenDelivery === 'cookies'
1327
- ? http.post(refreshUrl, {}, { withCredentials: true })
1328
- : from(authService.refresh());
1329
- return refresh$.pipe(switchMap((response) => {
1330
- if (config.debug) {
1331
- console.warn('[nauth-interceptor] Refresh successful');
1332
- }
1333
- isRefreshing = false;
1334
- // Get new token (JSON mode) or signal success (cookies mode)
1335
- const newToken = 'accessToken' in response ? response.accessToken : 'success';
1336
- refreshTokenSubject.next(newToken ?? 'success');
1337
- // #region agent log
1338
- fetch('http://127.0.0.1:7242/ingest/97f9fe53-6a8b-43e2-ae9b-4b2d0f725816', {
1339
- method: 'POST',
1340
- headers: { 'Content-Type': 'application/json' },
1341
- body: JSON.stringify({
1342
- location: 'auth.interceptor.ts:125',
1343
- message: 'Before buildRetryRequest',
1344
- data: {
1345
- authReqBody: authReq.body,
1346
- authReqMethod: authReq.method,
1347
- authReqUrl: authReq.url,
1348
- authReqBodyType: typeof authReq.body,
1349
- },
1350
- timestamp: Date.now(),
1351
- sessionId: 'debug-session',
1352
- hypothesisId: 'A',
1353
- }),
1354
- }).catch(() => { });
1355
- // #endregion
1356
- // Build retry request with fresh CSRF token (re-read from cookie after refresh)
1357
- const retryReq = buildRetryRequest(authReq, tokenDelivery, newToken, config.csrf);
1358
- // #region agent log
1359
- fetch('http://127.0.0.1:7242/ingest/97f9fe53-6a8b-43e2-ae9b-4b2d0f725816', {
1360
- method: 'POST',
1361
- headers: { 'Content-Type': 'application/json' },
1362
- body: JSON.stringify({
1363
- location: 'auth.interceptor.ts:130',
1364
- message: 'After buildRetryRequest',
1365
- data: {
1366
- retryReqBody: retryReq.body,
1367
- retryReqMethod: retryReq.method,
1368
- retryReqUrl: retryReq.url,
1369
- retryReqBodyType: typeof retryReq.body,
1370
- headersKeys: retryReq.headers.keys(),
1371
- },
1372
- timestamp: Date.now(),
1373
- sessionId: 'debug-session',
1374
- hypothesisId: 'B',
1375
- }),
1376
- }).catch(() => { });
1377
- // #endregion
1378
- if (config.debug) {
1379
- console.warn('[nauth-interceptor] Retrying:', req.url);
1380
- }
1381
- // Retry the request with fresh token/CSRF
1382
- // IMPORTANT: Errors from the retry (e.g., 400 validation) should NOT trigger
1383
- // session expiration redirect. Only the refresh failure should redirect.
1384
- return next(retryReq).pipe(catchError((retryErr) => {
1385
- // Retry failed (could be 400, 403, 500, etc.)
1386
- // Just propagate the error - don't redirect to login
1387
- if (config.debug) {
1388
- console.warn('[nauth-interceptor] Retry request failed:', retryErr);
1389
- }
1390
- return throwError(() => retryErr);
1391
- }));
1392
- }), catchError((err) => {
1393
- // This only catches REFRESH failures, not retry failures
1394
- if (config.debug) {
1395
- console.error('[nauth-interceptor] Refresh failed:', err);
1396
- }
1397
- isRefreshing = false;
1398
- refreshTokenSubject.next(null);
1399
- // Handle session expiration - redirect to configured URL
1400
- // Only redirect if refresh itself failed (not if retry failed)
1401
- if (config.redirects?.sessionExpired) {
1402
- router.navigateByUrl(config.redirects.sessionExpired).catch((navError) => {
1403
- if (config.debug) {
1404
- console.error('[nauth-interceptor] Navigation failed:', navError);
1405
- }
1406
- });
1407
- }
1408
- return throwError(() => err);
1409
- }));
1410
- }
1411
- else {
1412
- // Wait for ongoing refresh
1413
- if (config.debug) {
1414
- console.warn('[nauth-interceptor] Waiting for refresh...');
1415
- }
1416
- return refreshTokenSubject.pipe(filter$1((token) => token !== null), take(1), switchMap((token) => {
1417
- if (config.debug) {
1418
- console.warn('[nauth-interceptor] Refresh done, retrying:', req.url);
1419
- }
1420
- const retryReq = buildRetryRequest(authReq, tokenDelivery, token, config.csrf);
1421
- // Retry the request - errors here should propagate normally
1422
- // without triggering session expiration redirect
1423
- return next(retryReq).pipe(catchError((retryErr) => {
1424
- if (config.debug) {
1425
- console.warn('[nauth-interceptor] Retry request failed:', retryErr);
1426
- }
1427
- return throwError(() => retryErr);
1428
- }));
1429
- }));
1430
- }
1431
- }));
1342
+ return createNAuthAuthHttpInterceptor({ config, http, authService, router, next, req });
1432
1343
  };
1433
- /**
1434
- * Build retry request with appropriate auth.
1435
- *
1436
- * CRITICAL FIX: In cookies mode, after refresh the server may send updated cookies.
1437
- * We MUST re-read the CSRF token from the cookie before retrying to ensure we have
1438
- * the current CSRF token that matches what the server expects.
1439
- *
1440
- * In JSON mode: Clones the request and adds the new Bearer token.
1441
- *
1442
- * @param originalReq - The base request (already has withCredentials if cookies mode)
1443
- * @param tokenDelivery - 'cookies' or 'json'
1444
- * @param newToken - The new access token (JSON mode only)
1445
- * @param csrfConfig - CSRF configuration to re-read token from cookie
1446
- * @returns The request ready for retry with fresh auth
1447
- */
1448
- function buildRetryRequest(originalReq, tokenDelivery, newToken, csrfConfig) {
1449
- if (tokenDelivery === 'json' && newToken && newToken !== 'success') {
1450
- return originalReq.clone({
1451
- setHeaders: { Authorization: `Bearer ${newToken}` },
1452
- });
1453
- }
1454
- // Cookies mode: Browser automatically sends updated httpOnly cookies (access/refresh tokens).
1455
- // However, CSRF token must match the cookie value at the moment of retry.
1456
- // We ALWAYS re-read from document.cookie here (using defaults when csrfConfig
1457
- // is not provided) to avoid stale header values after refresh or across tabs.
1458
- if (tokenDelivery === 'cookies' && ['POST', 'PUT', 'PATCH', 'DELETE'].includes(originalReq.method)) {
1459
- const csrfCookieName = csrfConfig?.cookieName ?? 'nauth_csrf_token';
1460
- const csrfHeaderName = csrfConfig?.headerName ?? 'x-csrf-token';
1461
- const freshCsrfToken = getCsrfToken(csrfCookieName);
1462
- // #region agent log
1463
- fetch('http://127.0.0.1:7242/ingest/97f9fe53-6a8b-43e2-ae9b-4b2d0f725816', {
1464
- method: 'POST',
1465
- headers: { 'Content-Type': 'application/json' },
1466
- body: JSON.stringify({
1467
- location: 'auth.interceptor.ts:buildRetryRequest',
1468
- message: 'Inside buildRetryRequest cookies branch',
1469
- data: {
1470
- originalReqBody: originalReq.body,
1471
- originalReqBodyType: typeof originalReq.body,
1472
- freshCsrfToken: freshCsrfToken?.substring(0, 8),
1473
- method: originalReq.method,
1474
- },
1475
- timestamp: Date.now(),
1476
- sessionId: 'debug-session',
1477
- hypothesisId: 'C',
1478
- }),
1479
- }).catch(() => { });
1480
- // #endregion
1481
- if (freshCsrfToken) {
1482
- // Clone with fresh CSRF token in header
1483
- const cloned = originalReq.clone({
1484
- setHeaders: { [csrfHeaderName]: freshCsrfToken },
1485
- });
1486
- // #region agent log
1487
- fetch('http://127.0.0.1:7242/ingest/97f9fe53-6a8b-43e2-ae9b-4b2d0f725816', {
1488
- method: 'POST',
1489
- headers: { 'Content-Type': 'application/json' },
1490
- body: JSON.stringify({
1491
- location: 'auth.interceptor.ts:buildRetryRequest:afterClone',
1492
- message: 'After clone with setHeaders',
1493
- data: { clonedBody: cloned.body, clonedBodyType: typeof cloned.body, originalBody: originalReq.body },
1494
- timestamp: Date.now(),
1495
- sessionId: 'debug-session',
1496
- hypothesisId: 'D',
1497
- }),
1498
- }).catch(() => { });
1499
- // #endregion
1500
- return cloned;
1501
- }
1502
- }
1503
- // No changes needed (GET request or no CSRF token available)
1504
- return originalReq;
1505
- }
1506
1344
  /**
1507
1345
  * Class-based interceptor for NgModule compatibility.
1508
1346
  */