@acorex/platform 20.2.0-next.1 → 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 +71 -17
- package/fesm2022/acorex-platform-auth.mjs +301 -102
- package/fesm2022/acorex-platform-auth.mjs.map +1 -1
- package/fesm2022/{acorex-platform-themes-default-entity-master-list-view.component-DJdedfa_.mjs → acorex-platform-themes-default-entity-master-list-view.component-C8UhVBSM.mjs} +5 -3
- package/fesm2022/{acorex-platform-themes-default-entity-master-list-view.component-DJdedfa_.mjs.map → acorex-platform-themes-default-entity-master-list-view.component-C8UhVBSM.mjs.map} +1 -1
- package/fesm2022/acorex-platform-themes-default.mjs +29 -17
- package/fesm2022/acorex-platform-themes-default.mjs.map +1 -1
- package/fesm2022/acorex-platform-themes-shared.mjs +24 -11
- package/fesm2022/acorex-platform-themes-shared.mjs.map +1 -1
- package/package.json +1 -1
- package/themes/default/index.d.ts +2 -1
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
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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
|
-
|
|
252
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
|
5
|
-
import {
|
|
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) =>
|
|
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
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
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
|
-
|
|
274
|
-
|
|
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
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
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
|
-
|
|
350
|
+
catch (error) {
|
|
351
|
+
console.error('Signin error:', error);
|
|
313
352
|
this.status.next(AXPSessionStatus.Unauthenticated);
|
|
314
|
-
throw
|
|
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
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
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
|
-
|
|
340
|
-
|
|
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
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
406
|
-
const
|
|
407
|
-
const
|
|
408
|
-
localStorage.setItem(AXPSessionService.SESSION_KEY, JSON.stringify(
|
|
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
|
-
|
|
412
|
-
|
|
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.
|
|
547
|
+
this.currentTenantSubject.next(null);
|
|
419
548
|
//
|
|
420
|
-
this.
|
|
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.
|
|
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 =
|
|
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 =
|
|
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
|