@acorex/platform 20.6.3 → 20.6.5

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, Injector } from '@angular/core';
2
+ import { InjectionToken, TemplateRef, ViewContainerRef, ModuleWithProviders, Injector, Type } 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';
@@ -355,5 +355,230 @@ declare class AXPUnauthenticatedError extends Error {
355
355
  } | undefined);
356
356
  }
357
357
 
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 };
358
+ /**
359
+ * Supported content types for challenge display
360
+ */
361
+ type AXPChallengeContentType = 'image-url' | 'image-base64' | 'text' | 'audio-url';
362
+ /**
363
+ * Challenge data returned from the server
364
+ * Contains all information needed to display and submit a challenge
365
+ */
366
+ interface AXPLoginChallengeData {
367
+ /**
368
+ * Unique identifier for this challenge
369
+ * Must be sent back with credentials when submitting login
370
+ */
371
+ id: string;
372
+ /**
373
+ * The challenge content to display
374
+ * Could be an image URL, base64 encoded image, text, or audio URL
375
+ */
376
+ content: string;
377
+ /**
378
+ * Type of content for proper rendering
379
+ */
380
+ contentType: AXPChallengeContentType;
381
+ /**
382
+ * When this challenge expires (optional)
383
+ * After expiration, a new challenge should be fetched
384
+ */
385
+ expiresAt?: Date;
386
+ /**
387
+ * Additional metadata from server (optional)
388
+ */
389
+ metadata?: Record<string, unknown>;
390
+ }
391
+ /**
392
+ * Result of checking a login error response
393
+ * Determines if a challenge should be displayed
394
+ */
395
+ interface AXPChallengeCheckResult {
396
+ /**
397
+ * Whether a challenge is required
398
+ */
399
+ required: boolean;
400
+ /**
401
+ * Optional message to display to the user
402
+ */
403
+ message?: string;
404
+ /**
405
+ * Additional data from server that may be needed for getChallenge()
406
+ * For example: sessionId, attemptId, etc.
407
+ */
408
+ serverData?: Record<string, unknown>;
409
+ }
410
+
411
+ /**
412
+ * Base class for login challenge UI components
413
+ *
414
+ * Providers can extend this class to create custom challenge UIs.
415
+ * The login component will render this component and listen to its outputs.
416
+ *
417
+ * @example
418
+ * ```typescript
419
+ * @Component({
420
+ * selector: 'my-captcha-challenge',
421
+ * template: `
422
+ * <div class="captcha-container">
423
+ * <img [src]="'data:image/png;base64,' + challengeData().content" />
424
+ * <input
425
+ * type="text"
426
+ * [value]="response()"
427
+ * (input)="onResponseChange($event)"
428
+ * />
429
+ * <button (click)="onRefreshClick()">Refresh</button>
430
+ * </div>
431
+ * `
432
+ * })
433
+ * export class MyCaptchaChallengeComponent extends AXPLoginChallengeComponentBase {
434
+ * response = signal('');
435
+ *
436
+ * onResponseChange(event: Event) {
437
+ * const value = (event.target as HTMLInputElement).value;
438
+ * this.response.set(value);
439
+ * this.responseChange.emit(value);
440
+ * }
441
+ *
442
+ * onRefreshClick() {
443
+ * this.refreshRequest.emit();
444
+ * }
445
+ * }
446
+ * ```
447
+ */
448
+ declare abstract class AXPLoginChallengeComponentBase {
449
+ /**
450
+ * Challenge data to display
451
+ * Contains the image/content and metadata from the server
452
+ */
453
+ challengeData: i0.InputSignal<AXPLoginChallengeData>;
454
+ /**
455
+ * Whether the challenge is currently loading (e.g., refreshing)
456
+ */
457
+ isLoading: i0.InputSignal<boolean>;
458
+ /**
459
+ * Emits when the user enters or changes their response
460
+ * The login component will capture this value and include it in credentials
461
+ */
462
+ responseChange: i0.OutputEmitterRef<string>;
463
+ /**
464
+ * Emits when the user requests a new challenge (e.g., clicks refresh button)
465
+ * The login component will call provider.refreshChallenge()
466
+ */
467
+ refreshRequest: i0.OutputEmitterRef<void>;
468
+ static ɵfac: i0.ɵɵFactoryDeclaration<AXPLoginChallengeComponentBase, never>;
469
+ static ɵcmp: i0.ɵɵComponentDeclaration<AXPLoginChallengeComponentBase, "ng-component", never, { "challengeData": { "alias": "challengeData"; "required": true; "isSignal": true; }; "isLoading": { "alias": "isLoading"; "required": false; "isSignal": true; }; }, { "responseChange": "responseChange"; "refreshRequest": "refreshRequest"; }, never, never, true, never>;
470
+ }
471
+
472
+ /**
473
+ * Abstract base class for login challenge providers
474
+ *
475
+ * Implement this class to create custom challenge mechanisms like:
476
+ * - Image CAPTCHA
477
+ * - reCAPTCHA
478
+ * - SMS verification
479
+ * - Email verification
480
+ *
481
+ * @example
482
+ * ```typescript
483
+ * @Injectable()
484
+ * export class MyImageCaptchaProvider extends AXPLoginChallengeProvider {
485
+ * readonly name = 'image-captcha';
486
+ *
487
+ * checkResponse(error: unknown): AXPChallengeCheckResult | null {
488
+ * if (error instanceof HttpErrorResponse) {
489
+ * if (error.error?.requiresCaptcha) {
490
+ * return { required: true };
491
+ * }
492
+ * }
493
+ * return null;
494
+ * }
495
+ *
496
+ * async getChallenge(): Promise<AXPLoginChallengeData> {
497
+ * const response = await this.http.get('/api/captcha').toPromise();
498
+ * return {
499
+ * id: response.id,
500
+ * content: response.image,
501
+ * contentType: 'image-base64'
502
+ * };
503
+ * }
504
+ *
505
+ * async refreshChallenge(): Promise<AXPLoginChallengeData> {
506
+ * return this.getChallenge();
507
+ * }
508
+ *
509
+ * getChallengeComponent(): Type<AXPLoginChallengeComponentBase> {
510
+ * return MyCaptchaChallengeComponent;
511
+ * }
512
+ * }
513
+ * ```
514
+ */
515
+ declare abstract class AXPLoginChallengeProvider {
516
+ /**
517
+ * Unique name identifier for this provider
518
+ */
519
+ abstract readonly name: string;
520
+ /**
521
+ * Checks the login error response to determine if a challenge is required
522
+ *
523
+ * This method is called after a failed login attempt. The implementation
524
+ * should inspect the error and return a result indicating whether a
525
+ * challenge should be displayed.
526
+ *
527
+ * @param error - The error from the failed login attempt (type varies by implementation)
528
+ * @returns Challenge check result, or null if this provider doesn't handle this error
529
+ */
530
+ abstract checkResponse(error: unknown): AXPChallengeCheckResult | null;
531
+ /**
532
+ * Fetches a new challenge from the server
533
+ *
534
+ * Called when checkResponse indicates a challenge is required.
535
+ * Should make an API call to get challenge data (e.g., CAPTCHA image).
536
+ *
537
+ * @param serverData - Optional data from checkResponse result that may be needed
538
+ * @returns Promise resolving to challenge data for display
539
+ */
540
+ abstract getChallenge(serverData?: Record<string, unknown>): Promise<AXPLoginChallengeData>;
541
+ /**
542
+ * Fetches a fresh challenge, replacing the current one
543
+ *
544
+ * Called when user requests a new challenge (e.g., clicks "new image" button).
545
+ * Typically delegates to getChallenge() but may have different behavior.
546
+ *
547
+ * @returns Promise resolving to new challenge data
548
+ */
549
+ abstract refreshChallenge(): Promise<AXPLoginChallengeData>;
550
+ /**
551
+ * Returns the component type for rendering the challenge UI
552
+ *
553
+ * Override this method to provide a custom challenge UI component.
554
+ * If not overridden (returns null), the login component will use
555
+ * a default built-in UI.
556
+ *
557
+ * @returns Component type extending AXPLoginChallengeComponentBase, or null for default UI
558
+ */
559
+ getChallengeComponent(): Type<AXPLoginChallengeComponentBase> | null;
560
+ }
561
+
562
+ /**
563
+ * Injection token for the login challenge provider
564
+ *
565
+ * This token is optional - if not provided, no challenge mechanism will be used.
566
+ *
567
+ * @example
568
+ * ```typescript
569
+ * // In your app module or provider configuration:
570
+ * providers: [
571
+ * {
572
+ * provide: AXP_LOGIN_CHALLENGE_PROVIDER,
573
+ * useClass: MyImageCaptchaProvider
574
+ * }
575
+ * ]
576
+ *
577
+ * // In a component:
578
+ * private challengeProvider = inject(AXP_LOGIN_CHALLENGE_PROVIDER, { optional: true });
579
+ * ```
580
+ */
581
+ declare const AXP_LOGIN_CHALLENGE_PROVIDER: InjectionToken<AXPLoginChallengeProvider>;
582
+
583
+ export { AXPAuthGuard, AXPAuthModule, AXPAuthStrategy, AXPAuthStrategyRegistryService, AXPFeatureDirective, AXPFeatureGuard, AXPLoginChallengeComponentBase, AXPLoginChallengeProvider, AXPPermissionDefinitionBuilder, AXPPermissionDefinitionGroupBuilder, AXPPermissionDefinitionProviderContext, AXPPermissionDefinitionService, AXPPermissionDirective, AXPPermissionEvaluatorScopeProvider, AXPPermissionGuard, AXPSessionContext, AXPSessionService, AXPSessionStatus, AXPUnauthenticatedError, AXPUnauthorizedError, AXP_APPLICATION_LOADER, AXP_FEATURE_LOADER, AXP_LOGIN_CHALLENGE_PROVIDER, AXP_PERMISSION_DEFINITION_PROVIDER, AXP_PERMISSION_LOADER, AXP_TENANT_LOADER, JwtUtil, PkceUtil, TimeUtil, initializeAppState };
584
+ export type { AXPApplication, AXPApplicationLoader, AXPAuthModuleConfigs, AXPBaseCredentials, AXPChallengeCheckResult, AXPChallengeContentType, AXPFeature, AXPFeatureLoader, AXPLoginChallengeData, AXPPermission, AXPPermissionDefinition, AXPPermissionDefinitionProvider, AXPPermissionGroupDefinition, AXPPermissionLoader, AXPSessionData, AXPSignInResult, AXPTenant, AXPTenantLoader, AXPTokenResult, AXPUser };
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, Injector, Injectable, signal, inject, Input, Directive, provideAppInitializer, Optional, Inject, NgModule } from '@angular/core';
2
+ import { InjectionToken, Injector, Injectable, signal, inject, Input, Directive, provideAppInitializer, Optional, Inject, NgModule, input, output, Component } from '@angular/core';
3
3
  import { of, map, BehaviorSubject, shareReplay, defaultIfEmpty, switchMap, filter, firstValueFrom, first } from 'rxjs';
4
4
  import { AXPBroadcastEventService } from '@acorex/platform/core';
5
5
  import { isEmpty } from 'lodash-es';
@@ -984,9 +984,169 @@ class TimeUtil {
984
984
  }
985
985
  //#endregion
986
986
 
987
+ //#region ---- Challenge Data Types ----
988
+ //#endregion
989
+
990
+ //#region ---- Abstract Challenge Provider ----
991
+ /**
992
+ * Abstract base class for login challenge providers
993
+ *
994
+ * Implement this class to create custom challenge mechanisms like:
995
+ * - Image CAPTCHA
996
+ * - reCAPTCHA
997
+ * - SMS verification
998
+ * - Email verification
999
+ *
1000
+ * @example
1001
+ * ```typescript
1002
+ * @Injectable()
1003
+ * export class MyImageCaptchaProvider extends AXPLoginChallengeProvider {
1004
+ * readonly name = 'image-captcha';
1005
+ *
1006
+ * checkResponse(error: unknown): AXPChallengeCheckResult | null {
1007
+ * if (error instanceof HttpErrorResponse) {
1008
+ * if (error.error?.requiresCaptcha) {
1009
+ * return { required: true };
1010
+ * }
1011
+ * }
1012
+ * return null;
1013
+ * }
1014
+ *
1015
+ * async getChallenge(): Promise<AXPLoginChallengeData> {
1016
+ * const response = await this.http.get('/api/captcha').toPromise();
1017
+ * return {
1018
+ * id: response.id,
1019
+ * content: response.image,
1020
+ * contentType: 'image-base64'
1021
+ * };
1022
+ * }
1023
+ *
1024
+ * async refreshChallenge(): Promise<AXPLoginChallengeData> {
1025
+ * return this.getChallenge();
1026
+ * }
1027
+ *
1028
+ * getChallengeComponent(): Type<AXPLoginChallengeComponentBase> {
1029
+ * return MyCaptchaChallengeComponent;
1030
+ * }
1031
+ * }
1032
+ * ```
1033
+ */
1034
+ class AXPLoginChallengeProvider {
1035
+ /**
1036
+ * Returns the component type for rendering the challenge UI
1037
+ *
1038
+ * Override this method to provide a custom challenge UI component.
1039
+ * If not overridden (returns null), the login component will use
1040
+ * a default built-in UI.
1041
+ *
1042
+ * @returns Component type extending AXPLoginChallengeComponentBase, or null for default UI
1043
+ */
1044
+ getChallengeComponent() {
1045
+ return null;
1046
+ }
1047
+ }
1048
+ //#endregion
1049
+
1050
+ //#region ---- Injection Token ----
1051
+ /**
1052
+ * Injection token for the login challenge provider
1053
+ *
1054
+ * This token is optional - if not provided, no challenge mechanism will be used.
1055
+ *
1056
+ * @example
1057
+ * ```typescript
1058
+ * // In your app module or provider configuration:
1059
+ * providers: [
1060
+ * {
1061
+ * provide: AXP_LOGIN_CHALLENGE_PROVIDER,
1062
+ * useClass: MyImageCaptchaProvider
1063
+ * }
1064
+ * ]
1065
+ *
1066
+ * // In a component:
1067
+ * private challengeProvider = inject(AXP_LOGIN_CHALLENGE_PROVIDER, { optional: true });
1068
+ * ```
1069
+ */
1070
+ const AXP_LOGIN_CHALLENGE_PROVIDER = new InjectionToken('AXP_LOGIN_CHALLENGE_PROVIDER');
1071
+ //#endregion
1072
+
1073
+ //#region ---- Base Challenge Component ----
1074
+ /**
1075
+ * Base class for login challenge UI components
1076
+ *
1077
+ * Providers can extend this class to create custom challenge UIs.
1078
+ * The login component will render this component and listen to its outputs.
1079
+ *
1080
+ * @example
1081
+ * ```typescript
1082
+ * @Component({
1083
+ * selector: 'my-captcha-challenge',
1084
+ * template: `
1085
+ * <div class="captcha-container">
1086
+ * <img [src]="'data:image/png;base64,' + challengeData().content" />
1087
+ * <input
1088
+ * type="text"
1089
+ * [value]="response()"
1090
+ * (input)="onResponseChange($event)"
1091
+ * />
1092
+ * <button (click)="onRefreshClick()">Refresh</button>
1093
+ * </div>
1094
+ * `
1095
+ * })
1096
+ * export class MyCaptchaChallengeComponent extends AXPLoginChallengeComponentBase {
1097
+ * response = signal('');
1098
+ *
1099
+ * onResponseChange(event: Event) {
1100
+ * const value = (event.target as HTMLInputElement).value;
1101
+ * this.response.set(value);
1102
+ * this.responseChange.emit(value);
1103
+ * }
1104
+ *
1105
+ * onRefreshClick() {
1106
+ * this.refreshRequest.emit();
1107
+ * }
1108
+ * }
1109
+ * ```
1110
+ */
1111
+ class AXPLoginChallengeComponentBase {
1112
+ constructor() {
1113
+ //#region ---- Inputs ----
1114
+ /**
1115
+ * Challenge data to display
1116
+ * Contains the image/content and metadata from the server
1117
+ */
1118
+ this.challengeData = input.required(...(ngDevMode ? [{ debugName: "challengeData" }] : []));
1119
+ /**
1120
+ * Whether the challenge is currently loading (e.g., refreshing)
1121
+ */
1122
+ this.isLoading = input(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
1123
+ //#endregion
1124
+ //#region ---- Outputs ----
1125
+ /**
1126
+ * Emits when the user enters or changes their response
1127
+ * The login component will capture this value and include it in credentials
1128
+ */
1129
+ this.responseChange = output();
1130
+ /**
1131
+ * Emits when the user requests a new challenge (e.g., clicks refresh button)
1132
+ * The login component will call provider.refreshChallenge()
1133
+ */
1134
+ this.refreshRequest = output();
1135
+ }
1136
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPLoginChallengeComponentBase, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1137
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.3.12", type: AXPLoginChallengeComponentBase, isStandalone: true, selector: "ng-component", inputs: { challengeData: { classPropertyName: "challengeData", publicName: "challengeData", isSignal: true, isRequired: true, transformFunction: null }, isLoading: { classPropertyName: "isLoading", publicName: "isLoading", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { responseChange: "responseChange", refreshRequest: "refreshRequest" }, ngImport: i0, template: '', isInline: true }); }
1138
+ }
1139
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPLoginChallengeComponentBase, decorators: [{
1140
+ type: Component,
1141
+ args: [{
1142
+ template: '',
1143
+ standalone: true,
1144
+ }]
1145
+ }], propDecorators: { challengeData: [{ type: i0.Input, args: [{ isSignal: true, alias: "challengeData", required: true }] }], isLoading: [{ type: i0.Input, args: [{ isSignal: true, alias: "isLoading", required: false }] }], responseChange: [{ type: i0.Output, args: ["responseChange"] }], refreshRequest: [{ type: i0.Output, args: ["refreshRequest"] }] } });
1146
+
987
1147
  /**
988
1148
  * Generated bundle index. Do not edit.
989
1149
  */
990
1150
 
991
- 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 };
1151
+ export { AXPAuthGuard, AXPAuthModule, AXPAuthStrategy, AXPAuthStrategyRegistryService, AXPFeatureDirective, AXPFeatureGuard, AXPLoginChallengeComponentBase, AXPLoginChallengeProvider, AXPPermissionDefinitionBuilder, AXPPermissionDefinitionGroupBuilder, AXPPermissionDefinitionProviderContext, AXPPermissionDefinitionService, AXPPermissionDirective, AXPPermissionEvaluatorScopeProvider, AXPPermissionGuard, AXPSessionContext, AXPSessionService, AXPSessionStatus, AXPUnauthenticatedError, AXPUnauthorizedError, AXP_APPLICATION_LOADER, AXP_FEATURE_LOADER, AXP_LOGIN_CHALLENGE_PROVIDER, AXP_PERMISSION_DEFINITION_PROVIDER, AXP_PERMISSION_LOADER, AXP_TENANT_LOADER, JwtUtil, PkceUtil, TimeUtil, initializeAppState };
992
1152
  //# sourceMappingURL=acorex-platform-auth.mjs.map