@acontplus/ng-auth 1.1.2 → 1.1.3

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.
@@ -1,15 +1,15 @@
1
- import { UserRepository, BaseUseCase, LoggingService, TOKEN_PROVIDER } from '@acontplus/ng-infrastructure';
1
+ import { UserRepository, BaseUseCase, TOKEN_PROVIDER, LoggingService } from '@acontplus/ng-infrastructure';
2
2
  export { TOKEN_PROVIDER } from '@acontplus/ng-infrastructure';
3
3
  import * as i0 from '@angular/core';
4
4
  import { inject, PLATFORM_ID, Injectable, NgZone, signal, input, computed, ViewEncapsulation, ChangeDetectionStrategy, Component } from '@angular/core';
5
5
  import { Router } from '@angular/router';
6
+ import { of, tap, catchError, throwError, firstValueFrom, map, from } from 'rxjs';
6
7
  import * as i1 from '@angular/common';
7
8
  import { isPlatformBrowser, DOCUMENT, CommonModule } from '@angular/common';
8
9
  import { jwtDecode } from 'jwt-decode';
9
10
  import { ENVIRONMENT, AUTH_API } from '@acontplus/ng-config';
10
- import { catchError, switchMap } from 'rxjs/operators';
11
- import { throwError, firstValueFrom, map, from, of, tap, catchError as catchError$1 } from 'rxjs';
12
- import { HttpClient } from '@angular/common/http';
11
+ import { HttpClient, HttpContextToken } from '@angular/common/http';
12
+ import { catchError as catchError$1, switchMap } from 'rxjs/operators';
13
13
  import * as i2 from '@angular/forms';
14
14
  import { FormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
15
15
  import { MatCard, MatCardHeader, MatCardTitle, MatCardContent, MatCardFooter } from '@angular/material/card';
@@ -19,6 +19,9 @@ import { MatIcon } from '@angular/material/icon';
19
19
  import { MatButton, MatAnchor } from '@angular/material/button';
20
20
  import { MatCheckbox } from '@angular/material/checkbox';
21
21
 
22
+ class AuthRepository {
23
+ }
24
+
22
25
  class TokenRepository {
23
26
  environment = inject(ENVIRONMENT);
24
27
  platformId = inject(PLATFORM_ID);
@@ -137,271 +140,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImpor
137
140
  }]
138
141
  }] });
139
142
 
140
- /**
141
- * Service to manage URL redirection after authentication
142
- * Stores the intended URL when session is lost and redirects to it after successful login
143
- * SSR-compatible by checking platform before accessing sessionStorage
144
- */
145
- class UrlRedirectService {
146
- REDIRECT_URL_KEY = 'acp_redirect_url';
147
- EXCLUDED_ROUTES = [
148
- '/login',
149
- '/auth',
150
- '/register',
151
- '/forgot-password',
152
- '/reset-password',
153
- ];
154
- router = inject(Router);
155
- platformId = inject(PLATFORM_ID);
156
- document = inject(DOCUMENT);
157
- /**
158
- * Stores the current URL for later redirection
159
- * @param url - The URL to store (defaults to current URL)
160
- */
161
- storeIntendedUrl(url) {
162
- // Only store in browser environment
163
- if (!this.isBrowser()) {
164
- return;
165
- }
166
- const urlToStore = url || this.router.url;
167
- // Don't store authentication-related routes
168
- if (this.isExcludedRoute(urlToStore)) {
169
- return;
170
- }
171
- // Don't store URLs with query parameters that might contain sensitive data
172
- const urlWithoutParams = urlToStore.split('?')[0];
173
- this.getSessionStorage()?.setItem(this.REDIRECT_URL_KEY, urlWithoutParams);
174
- }
175
- /**
176
- * Gets the stored intended URL
177
- * @returns The stored URL or null if none exists
178
- */
179
- getIntendedUrl() {
180
- if (!this.isBrowser()) {
181
- return null;
182
- }
183
- return this.getSessionStorage()?.getItem(this.REDIRECT_URL_KEY) || null;
184
- }
185
- /**
186
- * Redirects to the stored URL and clears it from storage
187
- * @param defaultRoute - The default route to navigate to if no URL is stored
188
- */
189
- redirectToIntendedUrl(defaultRoute = '/') {
190
- const intendedUrl = this.getIntendedUrl();
191
- if (intendedUrl && !this.isExcludedRoute(intendedUrl)) {
192
- this.clearIntendedUrl();
193
- this.router.navigateByUrl(intendedUrl);
194
- }
195
- else {
196
- this.router.navigate([defaultRoute]);
197
- }
198
- }
199
- /**
200
- * Clears the stored intended URL
201
- */
202
- clearIntendedUrl() {
203
- if (!this.isBrowser()) {
204
- return;
205
- }
206
- this.getSessionStorage()?.removeItem(this.REDIRECT_URL_KEY);
207
- }
208
- /**
209
- * Checks if a URL should be excluded from redirection
210
- * @param url - The URL to check
211
- * @returns True if the URL should be excluded
212
- */
213
- isExcludedRoute(url) {
214
- return this.EXCLUDED_ROUTES.some(route => url.includes(route));
215
- }
216
- /**
217
- * Stores the current URL if it's not an excluded route
218
- * Useful for guards and interceptors
219
- */
220
- storeCurrentUrlIfAllowed() {
221
- const currentUrl = this.router.url;
222
- if (!this.isExcludedRoute(currentUrl)) {
223
- this.storeIntendedUrl(currentUrl);
224
- }
225
- }
226
- /**
227
- * Checks if we're running in a browser environment
228
- * @returns True if running in browser, false if SSR
229
- */
230
- isBrowser() {
231
- return isPlatformBrowser(this.platformId);
232
- }
233
- /**
234
- * Safely gets sessionStorage reference
235
- * @returns sessionStorage object or null if not available
236
- */
237
- getSessionStorage() {
238
- if (!this.isBrowser()) {
239
- return null;
240
- }
241
- try {
242
- return this.document.defaultView?.sessionStorage || null;
243
- }
244
- catch {
245
- // Handle cases where sessionStorage might be disabled
246
- return null;
247
- }
248
- }
249
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: UrlRedirectService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
250
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: UrlRedirectService, providedIn: 'root' });
251
- }
252
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: UrlRedirectService, decorators: [{
253
- type: Injectable,
254
- args: [{
255
- providedIn: 'root',
256
- }]
257
- }] });
258
-
259
- const authGuard = (_route, state) => {
260
- const tokenRepository = inject(TokenRepository);
261
- const router = inject(Router);
262
- const urlRedirectService = inject(UrlRedirectService);
263
- const environment = inject(ENVIRONMENT);
264
- if (tokenRepository.isAuthenticated()) {
265
- return true;
266
- }
267
- // Store the current URL for redirection after login
268
- urlRedirectService.storeIntendedUrl(state.url);
269
- // Redirect to login page (configurable via environment)
270
- router.navigate([environment.loginRoute]);
271
- return false;
272
- };
273
-
274
- /**
275
- * Interceptor that handles authentication errors and manages URL redirection
276
- * Captures the current URL when a 401 error occurs and redirects to login
277
- */
278
- const authRedirectInterceptor = (req, next) => {
279
- const router = inject(Router);
280
- const urlRedirectService = inject(UrlRedirectService);
281
- const tokenRepository = inject(TokenRepository);
282
- const environment = inject(ENVIRONMENT);
283
- return next(req).pipe(catchError((error) => {
284
- // Handle 401 Unauthorized errors
285
- if (error.status === 401) {
286
- // Only store and redirect if user was previously authenticated
287
- // This prevents redirect loops and handles session expiry scenarios
288
- if (tokenRepository.isAuthenticated()) {
289
- // Store the current URL for redirection after re-authentication
290
- urlRedirectService.storeCurrentUrlIfAllowed();
291
- // Navigate to login page
292
- router.navigate([environment.loginRoute]);
293
- }
294
- }
295
- // Re-throw the error so other error handlers can process it
296
- return throwError(() => error);
297
- }));
298
- };
299
-
300
- // src/lib/domain/models/auth.ts
301
-
302
- // src/lib/domain/models/index.ts
303
-
304
- class AuthRepository {
305
- }
306
-
307
- // src/lib/domain/repositories/index.ts
308
-
309
- // src/lib/domain/index.ts
310
-
311
- // src/lib/services/csrf.service.ts
312
- class CsrfService {
313
- http = inject(HttpClient);
314
- csrfToken = null;
315
- /**
316
- * Get CSRF token, fetching it if not available
317
- */
318
- async getCsrfToken() {
319
- if (this.csrfToken) {
320
- return this.csrfToken;
321
- }
322
- try {
323
- this.csrfToken = await firstValueFrom(this.http
324
- .get('/csrf-token')
325
- .pipe(map(response => response.csrfToken)));
326
- return this.csrfToken || '';
327
- }
328
- catch {
329
- // If CSRF endpoint fails, return empty token
330
- // Server should handle missing CSRF tokens appropriately
331
- return '';
332
- }
333
- }
334
- /**
335
- * Clear stored CSRF token (useful on logout)
336
- */
337
- clearCsrfToken() {
338
- this.csrfToken = null;
339
- }
340
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: CsrfService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
341
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: CsrfService, providedIn: 'root' });
342
- }
343
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: CsrfService, decorators: [{
344
- type: Injectable,
345
- args: [{
346
- providedIn: 'root',
347
- }]
348
- }] });
349
-
350
- // src/lib/data/repositories/auth-http.repository.ts
351
- function getDeviceInfo() {
352
- return `${navigator.platform ?? 'Unknown'} - ${navigator.userAgent}`;
353
- }
354
- class AuthHttpRepository extends AuthRepository {
355
- http = inject(HttpClient);
356
- csrfService = inject(CsrfService);
357
- URL = `${AUTH_API.ACCOUNT}`;
358
- login(request) {
359
- return from(this.csrfService.getCsrfToken()).pipe(switchMap(csrfToken => this.http.post(`${this.URL}login`, request, {
360
- headers: {
361
- 'Device-Info': getDeviceInfo(),
362
- 'X-CSRF-Token': csrfToken,
363
- },
364
- withCredentials: true,
365
- })));
366
- }
367
- register(request) {
368
- return from(this.csrfService.getCsrfToken()).pipe(switchMap(csrfToken => this.http.post(`${this.URL}register`, request, {
369
- headers: {
370
- 'Device-Info': getDeviceInfo(),
371
- 'X-CSRF-Token': csrfToken,
372
- },
373
- withCredentials: true,
374
- })));
375
- }
376
- refreshToken(request) {
377
- return from(this.csrfService.getCsrfToken()).pipe(switchMap(csrfToken => this.http.post(`${this.URL}refresh`, request, {
378
- headers: {
379
- 'Device-Info': getDeviceInfo(),
380
- 'X-CSRF-Token': csrfToken,
381
- },
382
- withCredentials: true,
383
- })));
384
- }
385
- logout(email, refreshToken) {
386
- return from(this.csrfService.getCsrfToken()).pipe(switchMap(csrfToken => this.http.post(`${this.URL}logout`, { email, refreshToken: refreshToken || undefined }, {
387
- headers: { 'X-CSRF-Token': csrfToken },
388
- withCredentials: true, // Ensure cookies are sent
389
- })));
390
- }
391
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: AuthHttpRepository, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
392
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: AuthHttpRepository, providedIn: 'root' });
393
- }
394
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: AuthHttpRepository, decorators: [{
395
- type: Injectable,
396
- args: [{
397
- providedIn: 'root',
398
- }]
399
- }] });
400
-
401
- // src/lib/data/repositories/index.ts
402
-
403
- // src/lib/data/index.ts
404
-
405
143
  // src/lib/presentation/stores/auth.store.ts
406
144
  class AuthStore {
407
145
  authRepository = inject(AuthRepository);
@@ -507,7 +245,7 @@ class AuthStore {
507
245
  })
508
246
  .pipe(tap(tokens => {
509
247
  this.setAuthenticated(tokens);
510
- }), catchError$1(() => {
248
+ }), catchError(() => {
511
249
  this.logout();
512
250
  return of(null);
513
251
  }), tap({
@@ -610,8 +348,127 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImpor
610
348
  }]
611
349
  }], ctorParameters: () => [] });
612
350
 
613
- // src/lib/application/use-cases/login.use-case.ts
614
- class LoginUseCase extends BaseUseCase {
351
+ /**
352
+ * Service to manage URL redirection after authentication
353
+ * Stores the intended URL when session is lost and redirects to it after successful login
354
+ * SSR-compatible by checking platform before accessing sessionStorage
355
+ */
356
+ class UrlRedirectService {
357
+ REDIRECT_URL_KEY = 'acp_redirect_url';
358
+ EXCLUDED_ROUTES = [
359
+ '/login',
360
+ '/auth',
361
+ '/register',
362
+ '/forgot-password',
363
+ '/reset-password',
364
+ ];
365
+ router = inject(Router);
366
+ platformId = inject(PLATFORM_ID);
367
+ document = inject(DOCUMENT);
368
+ /**
369
+ * Stores the current URL for later redirection
370
+ * @param url - The URL to store (defaults to current URL)
371
+ */
372
+ storeIntendedUrl(url) {
373
+ // Only store in browser environment
374
+ if (!this.isBrowser()) {
375
+ return;
376
+ }
377
+ const urlToStore = url || this.router.url;
378
+ // Don't store authentication-related routes
379
+ if (this.isExcludedRoute(urlToStore)) {
380
+ return;
381
+ }
382
+ // Don't store URLs with query parameters that might contain sensitive data
383
+ const urlWithoutParams = urlToStore.split('?')[0];
384
+ this.getSessionStorage()?.setItem(this.REDIRECT_URL_KEY, urlWithoutParams);
385
+ }
386
+ /**
387
+ * Gets the stored intended URL
388
+ * @returns The stored URL or null if none exists
389
+ */
390
+ getIntendedUrl() {
391
+ if (!this.isBrowser()) {
392
+ return null;
393
+ }
394
+ return this.getSessionStorage()?.getItem(this.REDIRECT_URL_KEY) || null;
395
+ }
396
+ /**
397
+ * Redirects to the stored URL and clears it from storage
398
+ * @param defaultRoute - The default route to navigate to if no URL is stored
399
+ */
400
+ redirectToIntendedUrl(defaultRoute = '/') {
401
+ const intendedUrl = this.getIntendedUrl();
402
+ if (intendedUrl && !this.isExcludedRoute(intendedUrl)) {
403
+ this.clearIntendedUrl();
404
+ this.router.navigateByUrl(intendedUrl);
405
+ }
406
+ else {
407
+ this.router.navigate([defaultRoute]);
408
+ }
409
+ }
410
+ /**
411
+ * Clears the stored intended URL
412
+ */
413
+ clearIntendedUrl() {
414
+ if (!this.isBrowser()) {
415
+ return;
416
+ }
417
+ this.getSessionStorage()?.removeItem(this.REDIRECT_URL_KEY);
418
+ }
419
+ /**
420
+ * Checks if a URL should be excluded from redirection
421
+ * @param url - The URL to check
422
+ * @returns True if the URL should be excluded
423
+ */
424
+ isExcludedRoute(url) {
425
+ return this.EXCLUDED_ROUTES.some(route => url.includes(route));
426
+ }
427
+ /**
428
+ * Stores the current URL if it's not an excluded route
429
+ * Useful for guards and interceptors
430
+ */
431
+ storeCurrentUrlIfAllowed() {
432
+ const currentUrl = this.router.url;
433
+ if (!this.isExcludedRoute(currentUrl)) {
434
+ this.storeIntendedUrl(currentUrl);
435
+ }
436
+ }
437
+ /**
438
+ * Checks if we're running in a browser environment
439
+ * @returns True if running in browser, false if SSR
440
+ */
441
+ isBrowser() {
442
+ return isPlatformBrowser(this.platformId);
443
+ }
444
+ /**
445
+ * Safely gets sessionStorage reference
446
+ * @returns sessionStorage object or null if not available
447
+ */
448
+ getSessionStorage() {
449
+ if (!this.isBrowser()) {
450
+ return null;
451
+ }
452
+ try {
453
+ return this.document.defaultView?.sessionStorage || null;
454
+ }
455
+ catch {
456
+ // Handle cases where sessionStorage might be disabled
457
+ return null;
458
+ }
459
+ }
460
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: UrlRedirectService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
461
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: UrlRedirectService, providedIn: 'root' });
462
+ }
463
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: UrlRedirectService, decorators: [{
464
+ type: Injectable,
465
+ args: [{
466
+ providedIn: 'root',
467
+ }]
468
+ }] });
469
+
470
+ // src/lib/application/use-cases/login.use-case.ts
471
+ class LoginUseCase extends BaseUseCase {
615
472
  authRepository = inject(AuthRepository);
616
473
  authStore = inject(AuthStore);
617
474
  router = inject(Router);
@@ -680,7 +537,7 @@ class RefreshTokenUseCase extends BaseUseCase {
680
537
  const rememberMe = this.tokenRepository.isRememberMeEnabled();
681
538
  // Update authentication state
682
539
  this.authStore.setAuthenticated(tokens, rememberMe);
683
- }), catchError$1(error => {
540
+ }), catchError(error => {
684
541
  // Don't logout here, let the interceptor handle it
685
542
  return throwError(() => error);
686
543
  }));
@@ -730,6 +587,202 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImpor
730
587
 
731
588
  // src/lib/application/index.ts
732
589
 
590
+ // src/lib/data/repositories/auth-http.repository.ts
591
+ function getDeviceInfo() {
592
+ return `${navigator.platform ?? 'Unknown'} - ${navigator.userAgent}`;
593
+ }
594
+ class AuthHttpRepository extends AuthRepository {
595
+ http = inject(HttpClient);
596
+ URL = `${AUTH_API.ACCOUNT}`;
597
+ login(request) {
598
+ return this.http.post(`${this.URL}login`, request, {
599
+ headers: {
600
+ 'Device-Info': getDeviceInfo(),
601
+ },
602
+ withCredentials: true,
603
+ });
604
+ }
605
+ register(request) {
606
+ return this.http.post(`${this.URL}register`, request, {
607
+ headers: {
608
+ 'Device-Info': getDeviceInfo(),
609
+ },
610
+ withCredentials: true,
611
+ });
612
+ }
613
+ refreshToken(request) {
614
+ return this.http.post(`${this.URL}refresh`, request, {
615
+ headers: {
616
+ 'Device-Info': getDeviceInfo(),
617
+ },
618
+ withCredentials: true,
619
+ });
620
+ }
621
+ logout(email, refreshToken) {
622
+ return this.http.post(`${this.URL}logout`, { email, refreshToken: refreshToken || undefined }, {
623
+ headers: {},
624
+ withCredentials: true, // Ensure cookies are sent
625
+ });
626
+ }
627
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: AuthHttpRepository, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
628
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: AuthHttpRepository, providedIn: 'root' });
629
+ }
630
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: AuthHttpRepository, decorators: [{
631
+ type: Injectable,
632
+ args: [{
633
+ providedIn: 'root',
634
+ }]
635
+ }] });
636
+
637
+ // src/lib/data/repositories/index.ts
638
+
639
+ // src/lib/data/index.ts
640
+
641
+ // src/lib/domain/models/auth.ts
642
+
643
+ // src/lib/domain/models/index.ts
644
+
645
+ // src/lib/domain/repositories/index.ts
646
+
647
+ // src/lib/domain/index.ts
648
+
649
+ const authGuard = (_route, state) => {
650
+ const tokenRepository = inject(TokenRepository);
651
+ const router = inject(Router);
652
+ const urlRedirectService = inject(UrlRedirectService);
653
+ const environment = inject(ENVIRONMENT);
654
+ if (tokenRepository.isAuthenticated()) {
655
+ return true;
656
+ }
657
+ // Store the current URL for redirection after login
658
+ urlRedirectService.storeIntendedUrl(state.url);
659
+ // Redirect to login page (configurable via environment)
660
+ router.navigate([environment.loginRoute]);
661
+ return false;
662
+ };
663
+
664
+ /**
665
+ * Interceptor that handles authentication errors and manages URL redirection
666
+ * Captures the current URL when a 401 error occurs and redirects to login
667
+ */
668
+ const authRedirectInterceptor = (req, next) => {
669
+ const router = inject(Router);
670
+ const urlRedirectService = inject(UrlRedirectService);
671
+ const tokenRepository = inject(TokenRepository);
672
+ const environment = inject(ENVIRONMENT);
673
+ return next(req).pipe(catchError$1((error) => {
674
+ // Handle 401 Unauthorized errors
675
+ if (error.status === 401) {
676
+ // Only store and redirect if user was previously authenticated
677
+ // This prevents redirect loops and handles session expiry scenarios
678
+ if (tokenRepository.isAuthenticated()) {
679
+ // Store the current URL for redirection after re-authentication
680
+ urlRedirectService.storeCurrentUrlIfAllowed();
681
+ // Navigate to login page
682
+ router.navigate([environment.loginRoute]);
683
+ }
684
+ }
685
+ // Re-throw the error so other error handlers can process it
686
+ return throwError(() => error);
687
+ }));
688
+ };
689
+
690
+ // src/lib/services/csrf.service.ts
691
+ class CsrfService {
692
+ http = inject(HttpClient);
693
+ csrfToken = null;
694
+ /**
695
+ * Get CSRF token, fetching it if not available
696
+ */
697
+ async getCsrfToken() {
698
+ if (this.csrfToken) {
699
+ return this.csrfToken;
700
+ }
701
+ try {
702
+ this.csrfToken = await firstValueFrom(this.http
703
+ .get('/csrf-token')
704
+ .pipe(map(response => response.csrfToken)));
705
+ return this.csrfToken || '';
706
+ }
707
+ catch {
708
+ // If CSRF endpoint fails, return empty token
709
+ // Server should handle missing CSRF tokens appropriately
710
+ return '';
711
+ }
712
+ }
713
+ /**
714
+ * Clear stored CSRF token (useful on logout)
715
+ */
716
+ clearCsrfToken() {
717
+ this.csrfToken = null;
718
+ }
719
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: CsrfService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
720
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: CsrfService, providedIn: 'root' });
721
+ }
722
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: CsrfService, decorators: [{
723
+ type: Injectable,
724
+ args: [{
725
+ providedIn: 'root',
726
+ }]
727
+ }] });
728
+
729
+ // A token to use with HttpContext for skipping CSRF token addition on specific requests.
730
+ const SKIP_CSRF = new HttpContextToken(() => false);
731
+ /**
732
+ * HTTP interceptor that automatically adds CSRF tokens to state-changing requests
733
+ * Only applies to requests to the same origin to avoid leaking tokens to external APIs
734
+ */
735
+ const csrfInterceptor = (req, next) => {
736
+ const csrfService = inject(CsrfService);
737
+ // Check if CSRF should be skipped for this request
738
+ const skipCsrf = req.context.get(SKIP_CSRF);
739
+ if (skipCsrf) {
740
+ return next(req);
741
+ }
742
+ // Only add CSRF token to state-changing requests (POST, PUT, PATCH, DELETE)
743
+ const isStateChangingMethod = ['POST', 'PUT', 'PATCH', 'DELETE'].includes(req.method.toUpperCase());
744
+ // Only add CSRF token to same-origin requests
745
+ const isSameOrigin = isRequestToSameOrigin(req);
746
+ if (isStateChangingMethod && isSameOrigin) {
747
+ return from(csrfService.getCsrfToken()).pipe(switchMap(csrfToken => {
748
+ const modifiedReq = req.clone({
749
+ setHeaders: {
750
+ 'X-CSRF-Token': csrfToken,
751
+ },
752
+ });
753
+ return next(modifiedReq);
754
+ }));
755
+ }
756
+ // For non-state-changing requests or external requests, proceed without modification
757
+ return next(req);
758
+ };
759
+ /**
760
+ * Checks if the request is going to the same origin as the current application
761
+ */
762
+ function isRequestToSameOrigin(req) {
763
+ try {
764
+ const requestUrl = new URL(req.url, window.location.origin);
765
+ return requestUrl.origin === window.location.origin;
766
+ }
767
+ catch {
768
+ // If URL parsing fails, assume it's not same origin for security
769
+ return false;
770
+ }
771
+ }
772
+
773
+ const authProviders = [
774
+ {
775
+ provide: AuthRepository,
776
+ useClass: AuthHttpRepository,
777
+ },
778
+ {
779
+ provide: TOKEN_PROVIDER,
780
+ useClass: TokenRepository,
781
+ },
782
+ ];
783
+
784
+ // src/lib/providers/index.ts
785
+
733
786
  // src/lib/presentation/stores/index.ts
734
787
 
735
788
  // src/lib/presentation/components/login/login.component.ts
@@ -856,22 +909,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImpor
856
909
 
857
910
  // src/lib/presentation/index.ts
858
911
 
859
- const authProviders = [
860
- {
861
- provide: AuthRepository,
862
- useClass: AuthHttpRepository,
863
- },
864
- {
865
- provide: TOKEN_PROVIDER,
866
- useClass: TokenRepository,
867
- },
868
- ];
869
-
870
- // src/lib/providers/index.ts
871
-
872
912
  /**
873
913
  * Generated bundle index. Do not edit.
874
914
  */
875
915
 
876
- export { AuthHttpRepository, AuthRepository, AuthStore, LoginComponent, LoginUseCase, LogoutUseCase, RefreshTokenUseCase, RegisterUseCase, TokenRepository, UrlRedirectService, authGuard, authProviders, authRedirectInterceptor };
916
+ export { AuthHttpRepository, AuthRepository, AuthStore, CsrfService, LoginComponent, LoginUseCase, LogoutUseCase, RefreshTokenUseCase, RegisterUseCase, SKIP_CSRF, TokenRepository, UrlRedirectService, authGuard, authProviders, authRedirectInterceptor, csrfInterceptor };
877
917
  //# sourceMappingURL=acontplus-ng-auth.mjs.map