@acorex/platform 20.2.0-next.0 → 20.2.0-next.2

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.
package/auth/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, TemplateRef, ViewContainerRef, ModuleWithProviders } from '@angular/core';
2
+ import { InjectionToken, TemplateRef, ViewContainerRef, ModuleWithProviders, Injector } from '@angular/core';
3
3
  import { Observable } from 'rxjs';
4
4
  import { AXPLogoConfig, AXPExpressionEvaluatorScopeProvider, AXPExpressionEvaluatorScopeProviderContext } from '@acorex/platform/core';
5
5
  import { CanActivateFn } from '@angular/router';
@@ -146,6 +146,7 @@ interface AXPTokenResult {
146
146
  accessToken: string;
147
147
  expiresIn?: string | null;
148
148
  refreshToken?: string;
149
+ idToken?: string;
149
150
  tenant?: AXPTenant | null;
150
151
  application?: AXPApplication | null;
151
152
  }
@@ -161,15 +162,14 @@ interface AXPSignInResult {
161
162
  application?: AXPApplication | null;
162
163
  };
163
164
  }
164
- interface AXPRefreshTokenResult {
165
- succeed: boolean;
166
- data?: AXPTokenResult;
167
- }
168
- interface AXPAuthStrategy {
169
- get name(): string;
170
- signin(credentials: AXPBaseCredentials): Promise<AXPSignInResult>;
171
- signout(): Promise<void>;
172
- refreshToken(context: AXPSessionContext): Promise<AXPRefreshTokenResult>;
165
+ declare abstract class AXPAuthStrategy {
166
+ abstract get name(): string;
167
+ abstract signin(credentials: AXPBaseCredentials): Promise<AXPSignInResult | void>;
168
+ abstract signout(): Promise<void>;
169
+ abstract refreshToken(context: AXPSessionContext): Promise<AXPSignInResult>;
170
+ abstract updateToken(params?: {
171
+ [key: string]: any;
172
+ }): Promise<AXPSignInResult | void>;
173
173
  }
174
174
  interface AXPSessionData {
175
175
  accessToken: string;
@@ -220,6 +220,8 @@ declare class AXPSessionService {
220
220
  private readonly applicationLoader;
221
221
  private status;
222
222
  readonly status$: Observable<AXPSessionStatus>;
223
+ private isLoading;
224
+ readonly isLoading$: Observable<boolean>;
223
225
  private currentUserSubject;
224
226
  readonly user$: Observable<AXPUser | null>;
225
227
  get user(): AXPUser | null;
@@ -238,25 +240,27 @@ declare class AXPSessionService {
238
240
  readonly features$: Observable<never[] | AXPFeature[]>;
239
241
  get features(): AXPFeature[];
240
242
  readonly isAuthenticated$: Observable<boolean>;
243
+ readonly isAuthenticatedWithLoading$: Observable<boolean>;
241
244
  readonly isAuthorized$: Observable<boolean>;
242
245
  restoreSession(): Promise<void>;
243
246
  signin(credentials: AXPBaseCredentials): Promise<void>;
247
+ updateToken(params?: {
248
+ [key: string]: any;
249
+ }): Promise<void>;
244
250
  signout(): Promise<void>;
245
251
  refreshToken(): Promise<any>;
246
- setTenant(tenant: AXPTenant | null): Promise<void>;
247
- setApplication(application: AXPApplication | null): Promise<void>;
248
252
  private loadPermissions;
249
253
  private loadFeatures;
250
254
  signInComplete(): Promise<void>;
251
- private setSession;
252
- private updateSession;
255
+ setSession(tokens: Partial<AXPSessionData>): void;
256
+ setStrategy(strategy: string): void;
253
257
  getSessionData(): AXPSessionData | null;
254
258
  private clearSession;
255
259
  authorize(...keys: string[]): boolean;
256
260
  isFeatureEnabled(...keys: string[]): boolean;
257
261
  getToken(): string | undefined;
258
262
  private checkTokenValidation;
259
- private getContext;
263
+ getContext(): AXPSessionContext;
260
264
  static ɵfac: i0.ɵɵFactoryDeclaration<AXPSessionService, never>;
261
265
  static ɵprov: i0.ɵɵInjectableDeclaration<AXPSessionService>;
262
266
  }
@@ -284,6 +288,56 @@ declare class AXPAuthModule {
284
288
  static ɵinj: i0.ɵɵInjectorDeclaration<AXPAuthModule>;
285
289
  }
286
290
 
291
+ declare class AXPAuthStrategyRegistryService {
292
+ private strategies;
293
+ private injector;
294
+ constructor(injector: Injector);
295
+ register(...plugins: (new () => AXPAuthStrategy)[]): void;
296
+ get(strategyKey: string): AXPAuthStrategy | undefined;
297
+ static ɵfac: i0.ɵɵFactoryDeclaration<AXPAuthStrategyRegistryService, never>;
298
+ static ɵprov: i0.ɵɵInjectableDeclaration<AXPAuthStrategyRegistryService>;
299
+ }
300
+
301
+ /**
302
+ * Utility class for JWT token operations
303
+ */
304
+ declare class JwtUtil {
305
+ /**
306
+ * Parses a JWT token and returns the payload
307
+ */
308
+ static parseJwt(token: string): any;
309
+ }
310
+ /**
311
+ * Utility class for PKCE (Proof Key for Code Exchange) operations
312
+ */
313
+ declare class PkceUtil {
314
+ /**
315
+ * Generates a random string for PKCE code verifier
316
+ */
317
+ static generateRandomString(length: number): string;
318
+ /**
319
+ * Generates PKCE code challenge from verifier
320
+ */
321
+ static generateCodeChallenge(codeVerifier: string): Promise<string>;
322
+ /**
323
+ * Base64 URL encoding for PKCE
324
+ */
325
+ private static base64UrlEncode;
326
+ }
327
+ /**
328
+ * Utility class for time and date operations
329
+ */
330
+ declare class TimeUtil {
331
+ /**
332
+ * Calculates the time difference in milliseconds between a future date and now
333
+ */
334
+ static expiresInMilliseconds(expiresInDate: string): number;
335
+ /**
336
+ * Calculates expiration date from seconds
337
+ */
338
+ static calculateExpireInDate(expireInSeconds: number): string;
339
+ }
340
+
287
341
  declare class AXPUnauthorizedError extends Error {
288
342
  data?: {
289
343
  redirectUrl?: string;
@@ -301,5 +355,5 @@ declare class AXPUnauthenticatedError extends Error {
301
355
  } | undefined);
302
356
  }
303
357
 
304
- export { AXPAuthGuard, AXPAuthModule, AXPFeatureDirective, AXPFeatureGuard, AXPPermissionDefinitionBuilder, AXPPermissionDefinitionGroupBuilder, AXPPermissionDefinitionProviderContext, AXPPermissionDefinitionService, AXPPermissionDirective, AXPPermissionEvaluatorScopeProvider, AXPPermissionGuard, AXPSessionContext, AXPSessionService, AXPSessionStatus, AXPUnauthenticatedError, AXPUnauthorizedError, AXP_APPLICATION_LOADER, AXP_FEATURE_LOADER, AXP_PERMISSION_DEFINITION_PROVIDER, AXP_PERMISSION_LOADER, AXP_TENANT_LOADER, initializeAppState };
305
- export type { AXPApplication, AXPApplicationLoader, AXPAuthModuleConfigs, AXPAuthStrategy, AXPBaseCredentials, AXPFeature, AXPFeatureLoader, AXPPermission, AXPPermissionDefinition, AXPPermissionDefinitionProvider, AXPPermissionGroupDefinition, AXPPermissionLoader, AXPRefreshTokenResult, AXPSessionData, AXPSignInResult, AXPTenant, AXPTenantLoader, AXPTokenResult, AXPUser };
358
+ export { AXPAuthGuard, AXPAuthModule, AXPAuthStrategy, AXPAuthStrategyRegistryService, AXPFeatureDirective, AXPFeatureGuard, AXPPermissionDefinitionBuilder, AXPPermissionDefinitionGroupBuilder, AXPPermissionDefinitionProviderContext, AXPPermissionDefinitionService, AXPPermissionDirective, AXPPermissionEvaluatorScopeProvider, AXPPermissionGuard, AXPSessionContext, AXPSessionService, AXPSessionStatus, AXPUnauthenticatedError, AXPUnauthorizedError, AXP_APPLICATION_LOADER, AXP_FEATURE_LOADER, AXP_PERMISSION_DEFINITION_PROVIDER, AXP_PERMISSION_LOADER, AXP_TENANT_LOADER, JwtUtil, PkceUtil, TimeUtil, initializeAppState };
359
+ export type { AXPApplication, AXPApplicationLoader, AXPAuthModuleConfigs, AXPBaseCredentials, AXPFeature, AXPFeatureLoader, AXPPermission, AXPPermissionDefinition, AXPPermissionDefinitionProvider, AXPPermissionGroupDefinition, AXPPermissionLoader, AXPSessionData, AXPSignInResult, AXPTenant, AXPTenantLoader, AXPTokenResult, AXPUser };
@@ -1,8 +1,8 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { InjectionToken, Injector, Injectable, signal, inject, Input, Directive, provideAppInitializer, Optional, Inject, NgModule } from '@angular/core';
3
- import { of, map, BehaviorSubject, shareReplay, defaultIfEmpty, firstValueFrom, first } from 'rxjs';
4
- import { AXPBroadcastEventService, AXP_EXPRESSION_EVALUATOR_SCOPE_PROVIDER } from '@acorex/platform/core';
5
- import { merge, isEmpty } from 'lodash-es';
3
+ import { of, map, BehaviorSubject, shareReplay, defaultIfEmpty, switchMap, filter, firstValueFrom, first } from 'rxjs';
4
+ import { AXPBroadcastEventService } from '@acorex/platform/core';
5
+ import { isEmpty } from 'lodash-es';
6
6
 
7
7
  const AXP_APPLICATION_LOADER = new InjectionToken('AXP_APPLICATION_LOADER', {
8
8
  providedIn: 'root',
@@ -21,6 +21,14 @@ class AXPApplicationDefaultLoader {
21
21
  editionName: 'Standard',
22
22
  features: [],
23
23
  },
24
+ {
25
+ id: '2',
26
+ name: 'default-app',
27
+ title: 'Default Application',
28
+ version: '1.0.0',
29
+ editionName: 'Standard',
30
+ features: [],
31
+ },
24
32
  ]);
25
33
  }
26
34
  }
@@ -216,6 +224,9 @@ class AXPSessionService {
216
224
  this.applicationLoader = inject(AXP_APPLICATION_LOADER);
217
225
  this.status = new BehaviorSubject(AXPSessionStatus.Unauthenticated);
218
226
  this.status$ = this.status.asObservable().pipe(shareReplay(1));
227
+ // Add loading state to prevent premature redirects
228
+ this.isLoading = new BehaviorSubject(true);
229
+ this.isLoading$ = this.isLoading.asObservable().pipe(shareReplay(1));
219
230
  this.currentUserSubject = new BehaviorSubject(null);
220
231
  this.user$ = this.currentUserSubject.asObservable().pipe(shareReplay(1));
221
232
  this.currentTenantSubject = new BehaviorSubject(null);
@@ -226,7 +237,17 @@ class AXPSessionService {
226
237
  this.permissions$ = this.permissionsSubject.asObservable().pipe(shareReplay(1), defaultIfEmpty([]));
227
238
  this.featuresSubject = new BehaviorSubject([]);
228
239
  this.features$ = this.featuresSubject.asObservable().pipe(shareReplay(1), defaultIfEmpty([]));
229
- this.isAuthenticated$ = this.status$.pipe(map((status) => status === AXPSessionStatus.Authenticated || status === AXPSessionStatus.Authorized), shareReplay(1));
240
+ this.isAuthenticated$ = this.status$.pipe(map((status) => {
241
+ const isAuth = status === AXPSessionStatus.Authenticated || status === AXPSessionStatus.Authorized;
242
+ return isAuth;
243
+ }), shareReplay(1));
244
+ // Add a new observable that considers loading state
245
+ this.isAuthenticatedWithLoading$ = this.isLoading$.pipe(switchMap((loading) => {
246
+ if (loading) {
247
+ return this.isLoading$.pipe(filter((isLoading) => !isLoading), switchMap(() => this.isAuthenticated$));
248
+ }
249
+ return this.isAuthenticated$;
250
+ }), shareReplay(1));
230
251
  this.isAuthorized$ = this.status$.pipe(map((status) => status === AXPSessionStatus.Authorized), shareReplay(1));
231
252
  }
232
253
  static { this.SESSION_KEY = 'AXP_SESSION'; }
@@ -264,108 +285,184 @@ class AXPSessionService {
264
285
  return this.featuresSubject.value ?? [];
265
286
  }
266
287
  async restoreSession() {
267
- const sessionData = this.getSessionData();
268
- if (sessionData) {
269
- if (sessionData.user) {
270
- this.currentUserSubject.next(sessionData.user);
271
- this.status.next(AXPSessionStatus.Authenticated);
288
+ this.isLoading.next(true);
289
+ try {
290
+ const sessionData = this.getSessionData();
291
+ if (sessionData) {
292
+ if (sessionData.user) {
293
+ this.currentUserSubject.next(sessionData.user);
294
+ this.status.next(AXPSessionStatus.Authenticated);
295
+ await this.loadPermissions();
296
+ await this.loadFeatures();
297
+ await this.signInComplete();
298
+ }
299
+ }
300
+ else {
301
+ this.status.next(AXPSessionStatus.Unauthorized);
272
302
  }
273
- // if (sessionData.tenant) {
274
- // this.setTenant(sessionData.tenant);
275
- // }
276
- // if (sessionData.application) {
277
- // this.setApplication(sessionData.application);
278
- // }
279
- await this.loadPermissions();
280
- await this.loadFeatures();
281
- await this.signInComplete();
282
- }
283
- else {
303
+ }
304
+ catch (error) {
284
305
  this.status.next(AXPSessionStatus.Unauthorized);
285
306
  }
307
+ finally {
308
+ this.isLoading.next(false);
309
+ }
286
310
  }
287
311
  async signin(credentials) {
288
- //
289
- this.clearSession();
290
- //
291
- const strategy = this.authStrategyRegistry.get(credentials.strategy);
292
- if (!strategy) {
293
- throw new Error(`Authentication strategy '${credentials.strategy}' is not supported`);
294
- }
295
- const result = await strategy.signin(credentials);
296
- if (result.succeed) {
297
- this.currentUserSubject.next(result.data.user);
298
- this.setSession({
299
- accessToken: result.data.accessToken,
300
- refreshToken: result.data.refreshToken,
301
- strategy: credentials.strategy,
302
- user: result.data.user,
303
- application: result.data?.application,
304
- tenant: result.data?.tenant,
305
- expiresIn: result.data?.expiresIn,
306
- idToken: result.data?.idToken ?? null,
307
- });
308
- this.status.next(AXPSessionStatus.Authenticated);
309
- if (this.application && this.tenant)
310
- await this.signInComplete();
312
+ this.isLoading.next(true);
313
+ try {
314
+ //
315
+ // this.clearSession();
316
+ //
317
+ const strategy = this.authStrategyRegistry.get(credentials.strategy);
318
+ if (!strategy) {
319
+ throw new Error(`Authentication strategy '${credentials.strategy}' is not supported`);
320
+ }
321
+ const result = await strategy.signin(credentials);
322
+ if (!result)
323
+ return;
324
+ if (result?.succeed) {
325
+ this.currentUserSubject.next(result.data.user);
326
+ this.setSession({
327
+ accessToken: result.data.accessToken,
328
+ refreshToken: result.data.refreshToken,
329
+ strategy: credentials.strategy,
330
+ user: result.data.user,
331
+ application: result.data?.application,
332
+ tenant: result.data?.tenant,
333
+ expiresIn: result.data?.expiresIn,
334
+ idToken: result.data?.idToken ?? null,
335
+ });
336
+ this.status.next(AXPSessionStatus.Authenticated);
337
+ // Load permissions and features
338
+ await this.loadPermissions();
339
+ await this.loadFeatures();
340
+ // If we have both tenant and application, complete the sign-in
341
+ if (this.application && this.tenant) {
342
+ await this.signInComplete();
343
+ }
344
+ }
345
+ else {
346
+ this.status.next(AXPSessionStatus.Unauthenticated);
347
+ throw new Error(`Invalid Username or Password`);
348
+ }
311
349
  }
312
- else {
350
+ catch (error) {
351
+ console.error('Signin error:', error);
313
352
  this.status.next(AXPSessionStatus.Unauthenticated);
314
- throw new Error(`Invalid Username or Password`);
353
+ throw error;
354
+ }
355
+ finally {
356
+ this.isLoading.next(false);
357
+ console.log('Signin process completed');
358
+ }
359
+ }
360
+ async updateToken(params) {
361
+ this.isLoading.next(true);
362
+ try {
363
+ const strategyName = this.getSessionData()?.strategy;
364
+ if (!strategyName) {
365
+ throw new Error('Strategy not found');
366
+ }
367
+ const strategy = this.authStrategyRegistry.get(strategyName);
368
+ if (!strategy) {
369
+ throw new Error(`Authentication strategy '${this.getSessionData()?.strategy}' is not supported`);
370
+ }
371
+ const result = await strategy.updateToken(params);
372
+ if (result?.succeed) {
373
+ this.currentUserSubject.next(result.data.user);
374
+ this.setSession({
375
+ accessToken: result.data.accessToken,
376
+ refreshToken: result.data.refreshToken,
377
+ strategy: strategyName,
378
+ user: result.data.user,
379
+ application: result.data?.application,
380
+ tenant: result.data?.tenant,
381
+ expiresIn: result.data?.expiresIn,
382
+ idToken: result.data?.idToken ?? null,
383
+ });
384
+ this.status.next(AXPSessionStatus.Authenticated);
385
+ // If we have both tenant and application, complete the sign-in
386
+ if (this.application && this.tenant) {
387
+ await this.signInComplete();
388
+ }
389
+ }
390
+ else {
391
+ this.status.next(AXPSessionStatus.Unauthenticated);
392
+ throw new Error(`Invalid Username or Password`);
393
+ }
394
+ }
395
+ catch (error) {
396
+ console.error('Update token error:', error);
397
+ throw error;
398
+ }
399
+ finally {
400
+ this.isLoading.next(false);
401
+ console.log('Update token process completed');
315
402
  }
316
403
  }
317
404
  async signout() {
318
- const sessionData = this.getSessionData();
319
- if (sessionData?.strategy) {
320
- const strategy = this.authStrategyRegistry.get(sessionData?.strategy);
321
- if (strategy) {
322
- await strategy.signout();
405
+ console.log('Signing out...');
406
+ this.isLoading.next(true);
407
+ try {
408
+ const sessionData = this.getSessionData();
409
+ if (sessionData?.strategy) {
410
+ const strategy = this.authStrategyRegistry.get(sessionData?.strategy);
411
+ if (strategy) {
412
+ try {
413
+ await strategy.signout();
414
+ }
415
+ catch (error) { }
416
+ }
323
417
  }
418
+ //
419
+ const userId = this.user?.id;
420
+ this.clearSession();
421
+ this.eventService.publish(AXPSessionStatus.SignedOut, { id: userId });
422
+ this.isLoading.next(false);
423
+ this.status.next(AXPSessionStatus.SignedOut);
424
+ }
425
+ finally {
426
+ this.isLoading.next(false);
324
427
  }
325
- //
326
- const userId = this.user?.id;
327
- this.clearSession();
328
- this.status.next(AXPSessionStatus.SignedOut);
329
- this.eventService.publish(AXPSessionStatus.SignedOut, { id: userId });
330
428
  }
331
429
  async refreshToken() {
430
+ console.log('Refreshing token...');
332
431
  return new Promise(async (resolve, reject) => {
333
432
  const sessionData = this.getSessionData();
334
433
  if (!sessionData || !sessionData?.refreshToken) {
434
+ console.log('No session data or refresh token found');
435
+ reject(new Error('No refresh token available'));
335
436
  return;
336
437
  }
337
438
  const strategy = this.authStrategyRegistry.get(sessionData.strategy);
338
439
  if (!strategy) {
339
- reject();
340
- throw new Error(`Authentication strategy '${sessionData.strategy}' is not found`);
440
+ console.error('Authentication strategy not found:', sessionData.strategy);
441
+ reject(new Error(`Authentication strategy '${sessionData.strategy}' is not found`));
442
+ return;
341
443
  }
342
- const result = await strategy.refreshToken(this.getContext());
343
- if (result.succeed) {
344
- this.setSession(result.data);
345
- resolve(result.data?.accessToken);
444
+ try {
445
+ const result = await strategy.refreshToken(this.getContext());
446
+ if (result.succeed) {
447
+ console.log('Token refresh successful');
448
+ this.setSession(result.data);
449
+ resolve(result.data?.accessToken);
450
+ }
451
+ else {
452
+ console.error('Token refresh failed');
453
+ this.clearSession();
454
+ this.status.next(AXPSessionStatus.Expired);
455
+ reject(new Error('Token refresh failed'));
456
+ }
346
457
  }
347
- else {
458
+ catch (error) {
459
+ console.error('Error during token refresh:', error);
348
460
  this.clearSession();
349
461
  this.status.next(AXPSessionStatus.Expired);
462
+ reject(error);
350
463
  }
351
464
  });
352
465
  }
353
- async setTenant(tenant) {
354
- this.currentTenantSubject.next(tenant);
355
- if (tenant) {
356
- this.updateSession({ tenant: tenant });
357
- await this.refreshToken();
358
- }
359
- }
360
- async setApplication(application) {
361
- this.currentApplicationSubject.next(application);
362
- if (application) {
363
- this.updateSession({ application: application });
364
- await this.refreshToken();
365
- await this.loadPermissions();
366
- await this.loadFeatures();
367
- }
368
- }
369
466
  async loadPermissions() {
370
467
  try {
371
468
  const permissions = await firstValueFrom(this.permissionLoader.getList(this.getContext()));
@@ -387,37 +484,69 @@ class AXPSessionService {
387
484
  }
388
485
  }
389
486
  async signInComplete() {
487
+ // Ensure we have the required data
488
+ if (!this.user) {
489
+ this.status.next(AXPSessionStatus.Unauthenticated);
490
+ return;
491
+ }
492
+ // Set status to Authorized
390
493
  this.status.next(AXPSessionStatus.Authorized);
494
+ // Double-check the status was set correctly
495
+ setTimeout(() => {
496
+ console.log('Status after timeout:', this.status.value);
497
+ }, 100);
391
498
  }
392
499
  setSession(tokens) {
393
500
  const sessionData = {
394
501
  accessToken: tokens.accessToken,
395
502
  refreshToken: tokens.refreshToken,
396
503
  strategy: tokens.strategy,
397
- user: this.user,
504
+ user: tokens.user,
398
505
  application: tokens.application,
399
506
  tenant: tokens.tenant,
400
507
  expiresIn: tokens.expiresIn,
401
508
  idToken: tokens.idToken,
402
509
  };
403
- this.updateSession(sessionData);
510
+ // Update subjects
511
+ if (tokens.user) {
512
+ this.currentUserSubject.next(tokens.user);
513
+ }
514
+ if (tokens.tenant) {
515
+ this.currentTenantSubject.next(tokens.tenant);
516
+ }
517
+ if (tokens.application) {
518
+ this.currentApplicationSubject.next(tokens.application);
519
+ }
520
+ localStorage.setItem(AXPSessionService.SESSION_KEY, JSON.stringify(sessionData));
404
521
  }
405
- updateSession(update) {
406
- const currentSession = this.getSessionData() ?? {};
407
- const updatedSession = merge({}, currentSession, update);
408
- localStorage.setItem(AXPSessionService.SESSION_KEY, JSON.stringify(updatedSession));
522
+ setStrategy(strategy) {
523
+ const sessionData = this.getSessionData();
524
+ const newSessionData = { ...sessionData, strategy };
525
+ localStorage.setItem(AXPSessionService.SESSION_KEY, JSON.stringify(newSessionData));
409
526
  }
410
527
  getSessionData() {
411
- const sessionDataString = localStorage.getItem(AXPSessionService.SESSION_KEY);
412
- return sessionDataString ? JSON.parse(sessionDataString) : null;
528
+ try {
529
+ const sessionDataString = localStorage.getItem(AXPSessionService.SESSION_KEY);
530
+ if (sessionDataString) {
531
+ const sessionData = JSON.parse(sessionDataString);
532
+ return sessionData;
533
+ }
534
+ else {
535
+ return null;
536
+ }
537
+ }
538
+ catch (error) {
539
+ localStorage.removeItem(AXPSessionService.SESSION_KEY);
540
+ return null;
541
+ }
413
542
  }
414
543
  clearSession() {
415
544
  //
416
545
  this.currentUserSubject.next(null);
417
546
  //
418
- this.setTenant(null);
547
+ this.currentTenantSubject.next(null);
419
548
  //
420
- this.setApplication(null);
549
+ this.currentApplicationSubject.next(null);
421
550
  //
422
551
  this.permissionsSubject.next([]);
423
552
  //
@@ -693,7 +822,7 @@ class AXPPermissionEvaluatorScopeProvider {
693
822
 
694
823
  const AXPAuthGuard = (route, state) => {
695
824
  const sessionService = inject(AXPSessionService);
696
- return sessionService.isAuthenticated$.pipe(first(), map((value) => {
825
+ return sessionService.isAuthenticatedWithLoading$.pipe(first(), map((value) => {
697
826
  if (value) {
698
827
  return true;
699
828
  }
@@ -758,14 +887,9 @@ class AXPAuthModule {
758
887
  static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.1.3", ngImport: i0, type: AXPAuthModule, declarations: [AXPPermissionDirective, AXPFeatureDirective], exports: [AXPPermissionDirective, AXPFeatureDirective] }); }
759
888
  static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: AXPAuthModule, providers: [
760
889
  provideAppInitializer(() => {
761
- const initializerFn = (initializeAppState)(inject(AXPSessionService));
890
+ const initializerFn = initializeAppState(inject(AXPSessionService));
762
891
  return initializerFn();
763
892
  }),
764
- {
765
- provide: AXP_EXPRESSION_EVALUATOR_SCOPE_PROVIDER,
766
- useClass: AXPPermissionEvaluatorScopeProvider,
767
- multi: true,
768
- },
769
893
  ] }); }
770
894
  }
771
895
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: AXPAuthModule, decorators: [{
@@ -776,14 +900,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImpor
776
900
  declarations: [AXPPermissionDirective, AXPFeatureDirective],
777
901
  providers: [
778
902
  provideAppInitializer(() => {
779
- const initializerFn = (initializeAppState)(inject(AXPSessionService));
903
+ const initializerFn = initializeAppState(inject(AXPSessionService));
780
904
  return initializerFn();
781
905
  }),
782
- {
783
- provide: AXP_EXPRESSION_EVALUATOR_SCOPE_PROVIDER,
784
- useClass: AXPPermissionEvaluatorScopeProvider,
785
- multi: true,
786
- },
787
906
  ],
788
907
  }]
789
908
  }], ctorParameters: () => [{ type: undefined, decorators: [{
@@ -793,9 +912,89 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImpor
793
912
  args: ['AXPAuthModuleFactory']
794
913
  }] }] });
795
914
 
915
+ class AXPAuthStrategy {
916
+ }
917
+
918
+ //#region ---- JWT Utility ----
919
+ /**
920
+ * Utility class for JWT token operations
921
+ */
922
+ class JwtUtil {
923
+ /**
924
+ * Parses a JWT token and returns the payload
925
+ */
926
+ static parseJwt(token) {
927
+ try {
928
+ const base64Url = token.split('.')[1];
929
+ const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
930
+ const jsonPayload = decodeURIComponent(atob(base64)
931
+ .split('')
932
+ .map(c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))
933
+ .join(''));
934
+ return JSON.parse(jsonPayload);
935
+ }
936
+ catch (error) {
937
+ throw new Error('Invalid JWT token');
938
+ }
939
+ }
940
+ }
941
+ //#endregion
942
+ //#region ---- PKCE Utility ----
943
+ /**
944
+ * Utility class for PKCE (Proof Key for Code Exchange) operations
945
+ */
946
+ class PkceUtil {
947
+ /**
948
+ * Generates a random string for PKCE code verifier
949
+ */
950
+ static generateRandomString(length) {
951
+ const array = new Uint8Array(length);
952
+ crypto.getRandomValues(array);
953
+ return this.base64UrlEncode(array);
954
+ }
955
+ /**
956
+ * Generates PKCE code challenge from verifier
957
+ */
958
+ static async generateCodeChallenge(codeVerifier) {
959
+ const encoder = new TextEncoder();
960
+ const data = encoder.encode(codeVerifier);
961
+ const digest = await crypto.subtle.digest('SHA-256', data);
962
+ return this.base64UrlEncode(new Uint8Array(digest));
963
+ }
964
+ /**
965
+ * Base64 URL encoding for PKCE
966
+ */
967
+ static base64UrlEncode(array) {
968
+ return btoa(String.fromCharCode(...array))
969
+ .replace(/\+/g, '-')
970
+ .replace(/\//g, '_')
971
+ .replace(/=/g, '');
972
+ }
973
+ }
974
+ //#endregion
975
+ //#region ---- Time Utility ----
976
+ /**
977
+ * Utility class for time and date operations
978
+ */
979
+ class TimeUtil {
980
+ /**
981
+ * Calculates the time difference in milliseconds between a future date and now
982
+ */
983
+ static expiresInMilliseconds(expiresInDate) {
984
+ return new Date(expiresInDate).getTime() - new Date().getTime();
985
+ }
986
+ /**
987
+ * Calculates expiration date from seconds
988
+ */
989
+ static calculateExpireInDate(expireInSeconds) {
990
+ return new Date(Date.now() + expireInSeconds * 1000).toISOString();
991
+ }
992
+ }
993
+ //#endregion
994
+
796
995
  /**
797
996
  * Generated bundle index. Do not edit.
798
997
  */
799
998
 
800
- export { AXPAuthGuard, AXPAuthModule, AXPFeatureDirective, AXPFeatureGuard, AXPPermissionDefinitionBuilder, AXPPermissionDefinitionGroupBuilder, AXPPermissionDefinitionProviderContext, AXPPermissionDefinitionService, AXPPermissionDirective, AXPPermissionEvaluatorScopeProvider, AXPPermissionGuard, AXPSessionContext, AXPSessionService, AXPSessionStatus, AXPUnauthenticatedError, AXPUnauthorizedError, AXP_APPLICATION_LOADER, AXP_FEATURE_LOADER, AXP_PERMISSION_DEFINITION_PROVIDER, AXP_PERMISSION_LOADER, AXP_TENANT_LOADER, initializeAppState };
999
+ export { AXPAuthGuard, AXPAuthModule, AXPAuthStrategy, AXPAuthStrategyRegistryService, AXPFeatureDirective, AXPFeatureGuard, AXPPermissionDefinitionBuilder, AXPPermissionDefinitionGroupBuilder, AXPPermissionDefinitionProviderContext, AXPPermissionDefinitionService, AXPPermissionDirective, AXPPermissionEvaluatorScopeProvider, AXPPermissionGuard, AXPSessionContext, AXPSessionService, AXPSessionStatus, AXPUnauthenticatedError, AXPUnauthorizedError, AXP_APPLICATION_LOADER, AXP_FEATURE_LOADER, AXP_PERMISSION_DEFINITION_PROVIDER, AXP_PERMISSION_LOADER, AXP_TENANT_LOADER, JwtUtil, PkceUtil, TimeUtil, initializeAppState };
801
1000
  //# sourceMappingURL=acorex-platform-auth.mjs.map