@igo2/auth 20.1.0-next.16 → 20.1.0-next.17

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,16 +1,16 @@
1
1
  import { HttpClient, HttpHeaders, HTTP_INTERCEPTORS } from '@angular/common/http';
2
2
  import * as i0 from '@angular/core';
3
- import { inject, Injector, Injectable, makeEnvironmentProviders } from '@angular/core';
3
+ import { inject, Injector, Injectable, InjectionToken, signal, makeEnvironmentProviders } from '@angular/core';
4
4
  import { BaseStorage, StorageScope, StorageService } from '@igo2/core/storage';
5
- import { ConfigService } from '@igo2/core/config';
6
- import { jwtDecode } from 'jwt-decode';
7
5
  import { Router } from '@angular/router';
6
+ import { ConfigService } from '@igo2/core/config';
8
7
  import { LanguageService } from '@igo2/core/language';
9
8
  import { MessageService } from '@igo2/core/message';
10
9
  import { Base64 } from '@igo2/utils';
11
- import { BehaviorSubject, of } from 'rxjs';
12
- import { tap, catchError, map } from 'rxjs/operators';
10
+ import { BehaviorSubject, tap, of, Observable, Subject, EMPTY } from 'rxjs';
11
+ import { finalize, tap as tap$1, catchError, switchMap, takeUntil, debounceTime, map } from 'rxjs/operators';
13
12
  import { globalCacheBusterNotifier } from 'ts-cacheable';
13
+ import { jwtDecode } from 'jwt-decode';
14
14
  import { Md5 } from 'ts-md5';
15
15
 
16
16
  class TokenService {
@@ -56,9 +56,42 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
56
56
  }]
57
57
  }], ctorParameters: () => [] });
58
58
 
59
+ const USER_AUTH_OPTIONS = new InjectionToken('USER_AUTH_OPTIONS');
60
+ class UserService {
61
+ http = inject(HttpClient);
62
+ options = inject(USER_AUTH_OPTIONS);
63
+ baseUrl;
64
+ _user$ = new BehaviorSubject(undefined);
65
+ user$ = this._user$.asObservable();
66
+ constructor() {
67
+ this.baseUrl = this.options.apiUrl;
68
+ }
69
+ getUser() {
70
+ return this.http
71
+ .get(this.baseUrl)
72
+ .pipe(tap((user) => this._user$.next(user)));
73
+ }
74
+ sync() {
75
+ return this.http
76
+ .get(`${this.baseUrl}/sync`)
77
+ .pipe(tap((user) => this._user$.next(user)));
78
+ }
79
+ updatePreference(preference) {
80
+ return this.http.patch(this.baseUrl, { preference });
81
+ }
82
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: UserService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
83
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: UserService });
84
+ }
85
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: UserService, decorators: [{
86
+ type: Injectable
87
+ }], ctorParameters: () => [] });
88
+
59
89
  class AuthService {
60
90
  http = inject(HttpClient);
61
91
  tokenService = inject(TokenService);
92
+ userService = inject(UserService, {
93
+ optional: true
94
+ });
62
95
  config = inject(ConfigService);
63
96
  languageService = inject(LanguageService);
64
97
  messageService = inject(MessageService);
@@ -67,8 +100,9 @@ class AuthService {
67
100
  logged$ = new BehaviorSubject(undefined);
68
101
  redirectUrl;
69
102
  languageForce = false;
70
- anonymous = false;
71
103
  authOptions;
104
+ anonymous = false;
105
+ isLogging = signal(false, ...(ngDevMode ? [{ debugName: "isLogging" }] : []));
72
106
  get hasAuthService() {
73
107
  return this.authOptions?.url !== undefined;
74
108
  }
@@ -78,26 +112,28 @@ class AuthService {
78
112
  }
79
113
  constructor() {
80
114
  this.authOptions = this.config.getConfig('auth');
81
- this.authenticate$.next(this.authenticated);
115
+ this.initializeAuthentication(this.authenticated).subscribe();
82
116
  this.authenticate$.subscribe((authenticated) => {
83
117
  this.logged$.next(authenticated);
84
118
  globalCacheBusterNotifier.next();
85
119
  });
86
120
  }
87
121
  login(username, password) {
122
+ this.isLogging.set(true);
88
123
  const myHeader = new HttpHeaders({ 'Content-Type': 'application/json' });
89
124
  const body = {
90
125
  username,
91
126
  password: this.encodePassword(password)
92
127
  };
93
- return this.loginCall(body, myHeader);
128
+ return this.loginCall(body, myHeader).pipe(finalize(() => this.isLogging.set(false)));
94
129
  }
95
- loginWithToken(token, type, infosUser) {
130
+ loginWithToken(token, type, infosUser, applicationId) {
96
131
  const myHeader = new HttpHeaders({ 'Content-Type': 'application/json' });
97
132
  const body = {
98
133
  token,
99
134
  typeConnection: type,
100
- infosUser
135
+ infosUser,
136
+ applicationId
101
137
  };
102
138
  return this.loginCall(body, myHeader);
103
139
  }
@@ -107,7 +143,7 @@ class AuthService {
107
143
  return of(true);
108
144
  }
109
145
  refresh() {
110
- return this.http.post(`${this.authOptions?.url}/refresh`, {}).pipe(tap((data) => {
146
+ return this.http.post(`${this.authOptions?.url}/refresh`, {}).pipe(tap$1((data) => {
111
147
  this.tokenService.set(data.token);
112
148
  }), catchError((err) => {
113
149
  err.error.caught = true;
@@ -115,10 +151,15 @@ class AuthService {
115
151
  }));
116
152
  }
117
153
  logout() {
154
+ this.logoutInternal();
155
+ if (this.authOptions.logoutRedirectRoute) {
156
+ this.router.navigate([this.authOptions.logoutRedirectRoute]);
157
+ }
158
+ }
159
+ logoutInternal() {
118
160
  this.anonymous = false;
119
161
  this.tokenService.remove();
120
162
  this.authenticate$.next(false);
121
- return of(true);
122
163
  }
123
164
  isAuthenticated() {
124
165
  return !this.tokenService.isExpired();
@@ -136,14 +177,8 @@ class AuthService {
136
177
  if (!this.router) {
137
178
  return;
138
179
  }
139
- const redirectUrl = this.redirectUrl || this.router.url;
140
- if (redirectUrl === this.authOptions.loginRoute) {
141
- const homeRoute = this.authOptions.homeRoute || '/';
142
- this.router.navigateByUrl(homeRoute);
143
- }
144
- else if (redirectUrl) {
145
- this.router.navigateByUrl(redirectUrl);
146
- }
180
+ const redirectUrl = this.redirectUrl ?? this.authOptions.homeRoute ?? '/';
181
+ this.router.navigateByUrl(redirectUrl);
147
182
  }
148
183
  getUserInfo() {
149
184
  const url = this.authOptions?.url + '/info';
@@ -155,6 +190,24 @@ class AuthService {
155
190
  updateUser(user) {
156
191
  return this.http.patch(this.authOptions?.url, user);
157
192
  }
193
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
194
+ translateError(prefix, error) {
195
+ return new Observable((observer) => {
196
+ try {
197
+ this.languageService.translate
198
+ .get(prefix + error.error.message)
199
+ .subscribe((errorMsg) => {
200
+ observer.next(errorMsg);
201
+ observer.complete();
202
+ });
203
+ }
204
+ catch {
205
+ if (error.error)
206
+ observer.next(error.error.message);
207
+ observer.complete();
208
+ }
209
+ });
210
+ }
158
211
  encodePassword(password) {
159
212
  return Base64.encode(password);
160
213
  }
@@ -178,7 +231,7 @@ class AuthService {
178
231
  loginCall(body, headers) {
179
232
  return this.http
180
233
  .post(`${this.authOptions?.url}/login`, body, { headers })
181
- .pipe(tap((data) => {
234
+ .pipe(tap$1((data) => {
182
235
  this.tokenService.set(data.token);
183
236
  const tokenDecoded = this.decodeToken();
184
237
  if (tokenDecoded?.user) {
@@ -186,14 +239,24 @@ class AuthService {
186
239
  this.languageService.setLanguage(tokenDecoded.user.locale);
187
240
  }
188
241
  if (tokenDecoded.user.isExpired) {
189
- this.messageService.alert('igo.auth.error.Password expired');
242
+ this.messageService.alert('igo.auth.error.intern.Password expired');
190
243
  }
191
244
  }
192
- this.authenticate$.next(true);
193
- }), catchError((err) => {
194
- err.error.caught = true;
195
- throw err;
196
- }));
245
+ }), switchMap(() => this.initializeAuthentication(true)));
246
+ }
247
+ initializeAuthentication(isAuthenticated) {
248
+ if (!isAuthenticated) {
249
+ this.authenticate$.next(false);
250
+ return of(null);
251
+ }
252
+ if (this.userService) {
253
+ const obs$ = this.authOptions.user.withSync
254
+ ? this.userService.sync()
255
+ : this.userService.getUser();
256
+ return obs$.pipe(tap$1(() => this.authenticate$.next(true)));
257
+ }
258
+ this.authenticate$.next(true);
259
+ return of(null);
197
260
  }
198
261
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: AuthService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
199
262
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: AuthService, providedIn: 'root' });
@@ -205,59 +268,92 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
205
268
  }]
206
269
  }], ctorParameters: () => [] });
207
270
 
271
+ class AdminGuard {
272
+ authService = inject(AuthService);
273
+ config = inject(ConfigService);
274
+ router = inject(Router);
275
+ canActivate(route, state) {
276
+ if (this.authService.isAdmin) {
277
+ return true;
278
+ }
279
+ this.authService.redirectUrl = state.url;
280
+ const authConfig = this.config.getConfig('auth');
281
+ if (authConfig?.loginRoute) {
282
+ this.router.navigateByUrl(authConfig.loginRoute);
283
+ }
284
+ return false;
285
+ }
286
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: AdminGuard, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
287
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: AdminGuard, providedIn: 'root' });
288
+ }
289
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: AdminGuard, decorators: [{
290
+ type: Injectable,
291
+ args: [{
292
+ providedIn: 'root'
293
+ }]
294
+ }] });
295
+
296
+ const DEBOUNCE_TIME = 3000; // 3 seconds;
208
297
  class AuthStorageService extends BaseStorage {
209
- http = inject(HttpClient);
210
298
  authService = inject(AuthService);
299
+ userService = inject(UserService, {
300
+ optional: true
301
+ });
211
302
  tokenService = inject(TokenService);
303
+ preferencesChanged$ = new Subject();
304
+ destroy$ = new Subject();
305
+ pendingPreferences = {};
306
+ serverPreferences = {};
212
307
  constructor() {
213
308
  const config = inject(ConfigService);
214
309
  super(config);
215
- this.authService.authenticate$.subscribe((isAuthenticated) => {
216
- if (isAuthenticated && this.options.url) {
217
- this.http
218
- .get(this.options.url)
219
- .subscribe((userIgo) => {
220
- if (userIgo && userIgo.preference) {
221
- for (const key of Object.keys(userIgo.preference)) {
222
- const value = userIgo.preference[key];
223
- super.set(key, value);
224
- }
225
- }
226
- });
310
+ this.authService.authenticate$
311
+ .pipe(switchMap((isAuthenticated) => {
312
+ if (isAuthenticated && this.userService) {
313
+ return this.userService.user$;
314
+ }
315
+ return EMPTY;
316
+ }), takeUntil(this.destroy$))
317
+ .subscribe((user) => {
318
+ if (!user?.preference) {
319
+ return;
227
320
  }
321
+ this.serverPreferences = { ...user.preference };
322
+ for (const key of Object.keys(user.preference)) {
323
+ const value = user.preference[key];
324
+ super.set(key, value);
325
+ }
326
+ });
327
+ this.preferencesChanged$
328
+ .pipe(debounceTime(DEBOUNCE_TIME), // Wait for more changes to accumulate
329
+ takeUntil(this.destroy$))
330
+ .subscribe(() => {
331
+ this.syncPreferences();
228
332
  });
229
333
  }
334
+ ngOnDestroy() {
335
+ this.destroy$.next();
336
+ this.destroy$.complete();
337
+ this.preferencesChanged$.complete();
338
+ }
230
339
  set(key, value, scope = StorageScope.LOCAL) {
231
- if (scope === StorageScope.LOCAL &&
232
- this.authService.authenticated &&
233
- this.options.url) {
234
- const preference = {};
235
- preference[key] = value;
236
- this.http.patch(this.options.url, { preference }).subscribe();
340
+ if (scope === StorageScope.LOCAL && this.authService.authenticated) {
341
+ this.pendingPreferences[key] = value;
342
+ this.preferencesChanged$.next();
237
343
  }
238
344
  super.set(key, value, scope);
239
345
  }
240
346
  remove(key, scope = StorageScope.LOCAL) {
241
- if (scope === StorageScope.LOCAL &&
242
- this.authService.authenticated &&
243
- this.options.url) {
244
- const preference = {};
245
- preference[key] = undefined;
246
- this.http.patch(this.options.url, { preference }).subscribe();
347
+ if (scope === StorageScope.LOCAL && this.authService.authenticated) {
348
+ this.pendingPreferences[key] = undefined;
349
+ this.preferencesChanged$.next();
247
350
  }
248
351
  super.remove(key, scope);
249
352
  }
250
353
  clear(scope = StorageScope.LOCAL) {
251
- if (scope === StorageScope.LOCAL &&
252
- this.authService.authenticated &&
253
- this.options.url) {
254
- this.http
255
- .patch(this.options.url, { preference: {} }, {
256
- params: {
257
- mergePreference: 'false'
258
- }
259
- })
260
- .subscribe();
354
+ if (scope === StorageScope.LOCAL && this.authService.authenticated) {
355
+ this.pendingPreferences = {};
356
+ this.preferencesChanged$.next();
261
357
  }
262
358
  let token;
263
359
  if (scope === StorageScope.LOCAL) {
@@ -267,18 +363,24 @@ class AuthStorageService extends BaseStorage {
267
363
  if (token) {
268
364
  this.tokenService.set(token);
269
365
  }
270
- if (scope === StorageScope.LOCAL &&
271
- this.authService.authenticated &&
272
- this.options.url) {
273
- this.http
274
- .get(this.options.url)
275
- .subscribe((userIgo) => {
276
- if (userIgo && userIgo.preference) {
277
- for (const key of Object.keys(userIgo.preference)) {
278
- const value = userIgo.preference[key];
279
- super.set(key, value);
280
- }
281
- }
366
+ }
367
+ syncPreferences() {
368
+ if (Object.keys(this.pendingPreferences).length === 0) {
369
+ return;
370
+ }
371
+ // Filter out preferences that haven't actually changed
372
+ const changedPreferences = {};
373
+ for (const key of Object.keys(this.pendingPreferences)) {
374
+ const newValue = this.pendingPreferences[key];
375
+ const oldValue = this.serverPreferences[key];
376
+ if (JSON.stringify(newValue) !== JSON.stringify(oldValue)) {
377
+ changedPreferences[key] = newValue;
378
+ }
379
+ }
380
+ this.pendingPreferences = {};
381
+ if (Object.keys(changedPreferences).length > 0) {
382
+ this.userService?.updatePreference(changedPreferences).subscribe(() => {
383
+ Object.assign(this.serverPreferences, changedPreferences);
282
384
  });
283
385
  }
284
386
  }
@@ -292,10 +394,30 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
292
394
  }]
293
395
  }], ctorParameters: () => [] });
294
396
 
295
- var AuthFeatureKind;
296
- (function (AuthFeatureKind) {
297
- AuthFeatureKind[AuthFeatureKind["Microsoft"] = 0] = "Microsoft";
298
- })(AuthFeatureKind || (AuthFeatureKind = {}));
397
+ class AuthGuard {
398
+ authService = inject(AuthService);
399
+ config = inject(ConfigService);
400
+ router = inject(Router);
401
+ canActivate(route, state) {
402
+ if (this.authService.authenticated) {
403
+ return true;
404
+ }
405
+ this.authService.redirectUrl = state.url;
406
+ const authConfig = this.config.getConfig('auth');
407
+ if (authConfig?.loginRoute) {
408
+ this.router.navigateByUrl(authConfig.loginRoute);
409
+ }
410
+ return false;
411
+ }
412
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: AuthGuard, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
413
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: AuthGuard, providedIn: 'root' });
414
+ }
415
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: AuthGuard, decorators: [{
416
+ type: Injectable,
417
+ args: [{
418
+ providedIn: 'root'
419
+ }]
420
+ }] });
299
421
 
300
422
  class AuthInterceptor {
301
423
  config = inject(ConfigService);
@@ -425,7 +547,9 @@ class AuthInterceptor {
425
547
  currentTime > jwt.exp - 1800) {
426
548
  this.refreshInProgress = true;
427
549
  const url = this.authOptions?.url;
428
- return this.http.post(`${url}/refresh`, {}).subscribe((data) => {
550
+ return this.http.post(`${url}/refresh`, {}).subscribe(
551
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
552
+ (data) => {
429
553
  this.tokenService.set(data.token);
430
554
  this.refreshInProgress = false;
431
555
  }, (err) => {
@@ -444,6 +568,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
444
568
  }]
445
569
  }], ctorParameters: () => [] });
446
570
 
571
+ var AuthFeatureKind;
572
+ (function (AuthFeatureKind) {
573
+ AuthFeatureKind[AuthFeatureKind["Microsoft"] = 0] = "Microsoft";
574
+ AuthFeatureKind[AuthFeatureKind["User"] = 1] = "User";
575
+ })(AuthFeatureKind || (AuthFeatureKind = {}));
576
+
447
577
  class LoggedGuard {
448
578
  authService = inject(AuthService);
449
579
  config = inject(ConfigService);
@@ -469,56 +599,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
469
599
  }]
470
600
  }] });
471
601
 
472
- class AuthGuard {
473
- authService = inject(AuthService);
474
- config = inject(ConfigService);
475
- router = inject(Router);
476
- canActivate(route, state) {
477
- if (this.authService.authenticated) {
478
- return true;
479
- }
480
- this.authService.redirectUrl = state.url;
481
- const authConfig = this.config.getConfig('auth');
482
- if (authConfig?.loginRoute) {
483
- this.router.navigateByUrl(authConfig.loginRoute);
484
- }
485
- return false;
486
- }
487
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: AuthGuard, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
488
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: AuthGuard, providedIn: 'root' });
489
- }
490
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: AuthGuard, decorators: [{
491
- type: Injectable,
492
- args: [{
493
- providedIn: 'root'
494
- }]
495
- }] });
496
-
497
- class AdminGuard {
498
- authService = inject(AuthService);
499
- config = inject(ConfigService);
500
- router = inject(Router);
501
- canActivate(route, state) {
502
- if (this.authService.isAdmin) {
503
- return true;
504
- }
505
- this.authService.redirectUrl = state.url;
506
- const authConfig = this.config.getConfig('auth');
507
- if (authConfig?.loginRoute) {
508
- this.router.navigateByUrl(authConfig.loginRoute);
509
- }
510
- return false;
511
- }
512
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: AdminGuard, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
513
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: AdminGuard, providedIn: 'root' });
514
- }
515
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: AdminGuard, decorators: [{
516
- type: Injectable,
517
- args: [{
518
- providedIn: 'root'
519
- }]
520
- }] });
521
-
522
602
  class ProfilsGuard {
523
603
  authService = inject(AuthService);
524
604
  config = inject(ConfigService);
@@ -548,6 +628,29 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
548
628
  }]
549
629
  }] });
550
630
 
631
+ function withUserIgo() {
632
+ return {
633
+ kind: AuthFeatureKind.User,
634
+ providers: [
635
+ {
636
+ provide: USER_AUTH_OPTIONS,
637
+ useFactory: (config) => config.getConfig('auth.user'),
638
+ deps: [ConfigService]
639
+ },
640
+ {
641
+ provide: UserService,
642
+ useFactory: (options) => {
643
+ if (!options) {
644
+ return undefined;
645
+ }
646
+ return new UserService();
647
+ },
648
+ deps: [USER_AUTH_OPTIONS]
649
+ }
650
+ ]
651
+ };
652
+ }
653
+
551
654
  function provideAuthentification(...features) {
552
655
  const providers = [
553
656
  {
@@ -574,5 +677,5 @@ function provideAuthentification(...features) {
574
677
  * Generated bundle index. Do not edit.
575
678
  */
576
679
 
577
- export { AdminGuard, AuthFeatureKind, AuthGuard, AuthInterceptor, AuthService, AuthStorageService, LoggedGuard, ProfilsGuard, TokenService, provideAuthentification };
680
+ export { AdminGuard, AuthFeatureKind, AuthGuard, AuthInterceptor, AuthService, AuthStorageService, LoggedGuard, ProfilsGuard, TokenService, USER_AUTH_OPTIONS, UserService, provideAuthentification, withUserIgo };
578
681
  //# sourceMappingURL=igo2-auth.mjs.map