@acorex-platform/framework-client 0.0.18 → 0.0.21
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/fesm2022/acorex-platform-framework-client-auth.mjs +872 -314
- package/fesm2022/acorex-platform-framework-client-auth.mjs.map +1 -1
- package/fesm2022/{acorex-platform-framework-client-common-common-settings.provider-Euotv9l0.mjs → acorex-platform-framework-client-common-common-settings.provider-CLm2BVk-.mjs} +88 -5
- package/fesm2022/acorex-platform-framework-client-common-common-settings.provider-CLm2BVk-.mjs.map +1 -0
- package/fesm2022/acorex-platform-framework-client-common.mjs +2666 -693
- package/fesm2022/acorex-platform-framework-client-common.mjs.map +1 -1
- package/fesm2022/acorex-platform-framework-client-core.mjs +685 -721
- package/fesm2022/acorex-platform-framework-client-core.mjs.map +1 -1
- package/fesm2022/acorex-platform-framework-client-domain.mjs +16 -16
- package/fesm2022/acorex-platform-framework-client-layout-builder.mjs +124 -72
- package/fesm2022/acorex-platform-framework-client-layout-builder.mjs.map +1 -1
- package/fesm2022/{acorex-platform-framework-client-layout-components-binding-expression-editor-popup.component-CjnQ9hAJ.mjs → acorex-platform-framework-client-layout-components-binding-expression-editor-popup.component-oE8CeLE8.mjs} +8 -6
- package/fesm2022/{acorex-platform-framework-client-layout-components-binding-expression-editor-popup.component-CjnQ9hAJ.mjs.map → acorex-platform-framework-client-layout-components-binding-expression-editor-popup.component-oE8CeLE8.mjs.map} +1 -1
- package/fesm2022/{acorex-platform-framework-client-layout-components-file-provider-picker.popup.component-fFen8w8M.mjs → acorex-platform-framework-client-layout-components-file-provider-picker.popup.component-BtLEUWAA.mjs} +4 -4
- package/fesm2022/{acorex-platform-framework-client-layout-components-file-provider-picker.popup.component-fFen8w8M.mjs.map → acorex-platform-framework-client-layout-components-file-provider-picker.popup.component-BtLEUWAA.mjs.map} +1 -1
- package/fesm2022/acorex-platform-framework-client-layout-components.mjs +2988 -857
- package/fesm2022/acorex-platform-framework-client-layout-components.mjs.map +1 -1
- package/fesm2022/acorex-platform-framework-client-layout-designer.mjs +277 -185
- package/fesm2022/acorex-platform-framework-client-layout-designer.mjs.map +1 -1
- package/fesm2022/acorex-platform-framework-client-layout-entity-acorex-platform-framework-client-layout-entity-BXOMs6oD.mjs +29184 -0
- package/fesm2022/acorex-platform-framework-client-layout-entity-acorex-platform-framework-client-layout-entity-BXOMs6oD.mjs.map +1 -0
- package/fesm2022/{acorex-platform-framework-client-layout-entity-attachments-page.component-BzUM-JMC.mjs → acorex-platform-framework-client-layout-entity-attachments-page.component-DCK-qb-F.mjs} +34 -20
- package/fesm2022/acorex-platform-framework-client-layout-entity-attachments-page.component-DCK-qb-F.mjs.map +1 -0
- package/fesm2022/acorex-platform-framework-client-layout-entity-bulk-entity.command-Cs3agnpF.mjs +162 -0
- package/fesm2022/acorex-platform-framework-client-layout-entity-bulk-entity.command-Cs3agnpF.mjs.map +1 -0
- package/fesm2022/{acorex-platform-framework-client-layout-entity-file-list-popup.component-Ci_CTFtq.mjs → acorex-platform-framework-client-layout-entity-file-list-popup.component-p0hxDb0W.mjs} +31 -19
- package/fesm2022/acorex-platform-framework-client-layout-entity-file-list-popup.component-p0hxDb0W.mjs.map +1 -0
- package/fesm2022/{acorex-platform-framework-client-layout-entity-gallery-page.component-DmiB__JR.mjs → acorex-platform-framework-client-layout-entity-gallery-page.component-DYV9YPyw.mjs} +30 -18
- package/fesm2022/acorex-platform-framework-client-layout-entity-gallery-page.component-DYV9YPyw.mjs.map +1 -0
- package/fesm2022/acorex-platform-framework-client-layout-entity.mjs +2 -27090
- package/fesm2022/acorex-platform-framework-client-layout-entity.mjs.map +1 -1
- package/fesm2022/acorex-platform-framework-client-layout-views.mjs +382 -154
- package/fesm2022/acorex-platform-framework-client-layout-views.mjs.map +1 -1
- package/fesm2022/acorex-platform-framework-client-layout-widget-core.mjs +646 -280
- package/fesm2022/acorex-platform-framework-client-layout-widget-core.mjs.map +1 -1
- package/fesm2022/{acorex-platform-framework-client-layout-widgets-button-widget-designer.component-CIwpAXbL.mjs → acorex-platform-framework-client-layout-widgets-button-widget-designer.component-BiWerYcu.mjs} +16 -10
- package/fesm2022/{acorex-platform-framework-client-layout-widgets-button-widget-designer.component-CIwpAXbL.mjs.map → acorex-platform-framework-client-layout-widgets-button-widget-designer.component-BiWerYcu.mjs.map} +1 -1
- package/fesm2022/{acorex-platform-framework-client-layout-widgets-image-preview.popup-CazpERbX.mjs → acorex-platform-framework-client-layout-widgets-image-preview.popup-BOrrMm3U.mjs} +9 -8
- package/fesm2022/acorex-platform-framework-client-layout-widgets-image-preview.popup-BOrrMm3U.mjs.map +1 -0
- package/fesm2022/{acorex-platform-framework-client-layout-widgets-page-widget-designer.component-DKkJi40s.mjs → acorex-platform-framework-client-layout-widgets-page-widget-designer.component-WzIiwnGC.mjs} +20 -12
- package/fesm2022/{acorex-platform-framework-client-layout-widgets-page-widget-designer.component-DKkJi40s.mjs.map → acorex-platform-framework-client-layout-widgets-page-widget-designer.component-WzIiwnGC.mjs.map} +1 -1
- package/fesm2022/{acorex-platform-framework-client-layout-widgets-repeater-widget-column.component-DtYZfA79.mjs → acorex-platform-framework-client-layout-widgets-repeater-widget-column.component-3CO4xJB3.mjs} +32 -18
- package/fesm2022/{acorex-platform-framework-client-layout-widgets-repeater-widget-column.component-DtYZfA79.mjs.map → acorex-platform-framework-client-layout-widgets-repeater-widget-column.component-3CO4xJB3.mjs.map} +1 -1
- package/fesm2022/{acorex-platform-framework-client-layout-widgets-tabular-data-edit-popup.component-nMUtxAyy.mjs → acorex-platform-framework-client-layout-widgets-tabular-data-edit-popup.component-CRST84ou.mjs} +8 -7
- package/fesm2022/{acorex-platform-framework-client-layout-widgets-tabular-data-edit-popup.component-nMUtxAyy.mjs.map → acorex-platform-framework-client-layout-widgets-tabular-data-edit-popup.component-CRST84ou.mjs.map} +1 -1
- package/fesm2022/{acorex-platform-framework-client-layout-widgets-tabular-data-view-popup.component-DvUSZ4hJ.mjs → acorex-platform-framework-client-layout-widgets-tabular-data-view-popup.component-FjtSRxIX.mjs} +4 -4
- package/fesm2022/{acorex-platform-framework-client-layout-widgets-tabular-data-view-popup.component-DvUSZ4hJ.mjs.map → acorex-platform-framework-client-layout-widgets-tabular-data-view-popup.component-FjtSRxIX.mjs.map} +1 -1
- package/fesm2022/{acorex-platform-framework-client-layout-widgets-text-block-widget-designer.component-BihIfkZW.mjs → acorex-platform-framework-client-layout-widgets-text-block-widget-designer.component-D6HFPmGC.mjs} +10 -8
- package/fesm2022/{acorex-platform-framework-client-layout-widgets-text-block-widget-designer.component-BihIfkZW.mjs.map → acorex-platform-framework-client-layout-widgets-text-block-widget-designer.component-D6HFPmGC.mjs.map} +1 -1
- package/fesm2022/acorex-platform-framework-client-layout-widgets.mjs +4868 -2548
- package/fesm2022/acorex-platform-framework-client-layout-widgets.mjs.map +1 -1
- package/fesm2022/acorex-platform-framework-client-native.mjs +7 -7
- package/fesm2022/acorex-platform-framework-client-realtime-set-presence-availability.command-CetlUjnM.mjs +29 -0
- package/fesm2022/acorex-platform-framework-client-realtime-set-presence-availability.command-CetlUjnM.mjs.map +1 -0
- package/fesm2022/acorex-platform-framework-client-realtime.mjs +463 -26
- package/fesm2022/acorex-platform-framework-client-realtime.mjs.map +1 -1
- package/fesm2022/acorex-platform-framework-client-runtime.mjs +300 -137
- package/fesm2022/acorex-platform-framework-client-runtime.mjs.map +1 -1
- package/fesm2022/{acorex-platform-framework-client-themes-default-entity-master-create-view.component-QIFX11dh.mjs → acorex-platform-framework-client-themes-default-entity-master-create-view.component-yeftgJ_K.mjs} +10 -7
- package/fesm2022/{acorex-platform-framework-client-themes-default-entity-master-create-view.component-QIFX11dh.mjs.map → acorex-platform-framework-client-themes-default-entity-master-create-view.component-yeftgJ_K.mjs.map} +1 -1
- package/fesm2022/{acorex-platform-framework-client-themes-default-entity-master-modify-view.component-BBdellyL.mjs → acorex-platform-framework-client-themes-default-entity-master-modify-view.component-DWE3OWGm.mjs} +10 -7
- package/fesm2022/{acorex-platform-framework-client-themes-default-entity-master-modify-view.component-BBdellyL.mjs.map → acorex-platform-framework-client-themes-default-entity-master-modify-view.component-DWE3OWGm.mjs.map} +1 -1
- package/fesm2022/{acorex-platform-framework-client-themes-default-entity-master-single-view.component-CIf1VWNW.mjs → acorex-platform-framework-client-themes-default-entity-master-single-view.component-C2Abf7Lb.mjs} +21 -19
- package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-single-view.component-C2Abf7Lb.mjs.map +1 -0
- package/fesm2022/{acorex-platform-framework-client-themes-default-error-401.component-C_czT_JC.mjs → acorex-platform-framework-client-themes-default-error-401.component-BrTotLx1.mjs} +5 -5
- package/fesm2022/{acorex-platform-framework-client-themes-default-error-401.component-C_czT_JC.mjs.map → acorex-platform-framework-client-themes-default-error-401.component-BrTotLx1.mjs.map} +1 -1
- package/fesm2022/{acorex-platform-framework-client-themes-default-error-404.component-C2Wu0YtF.mjs → acorex-platform-framework-client-themes-default-error-404.component-DsC7_s59.mjs} +5 -5
- package/fesm2022/{acorex-platform-framework-client-themes-default-error-404.component-C2Wu0YtF.mjs.map → acorex-platform-framework-client-themes-default-error-404.component-DsC7_s59.mjs.map} +1 -1
- package/fesm2022/{acorex-platform-framework-client-themes-default-error-offline.component-DRz-kVsT.mjs → acorex-platform-framework-client-themes-default-error-offline.component-CngiQycG.mjs} +6 -6
- package/fesm2022/acorex-platform-framework-client-themes-default-error-offline.component-CngiQycG.mjs.map +1 -0
- package/fesm2022/acorex-platform-framework-client-themes-default.mjs +421 -265
- package/fesm2022/acorex-platform-framework-client-themes-default.mjs.map +1 -1
- package/fesm2022/{acorex-platform-framework-client-themes-shared-icon-chooser-column.component-Ci_oH7NE.mjs → acorex-platform-framework-client-themes-shared-icon-chooser-column.component-CbZALjB5.mjs} +8 -6
- package/fesm2022/{acorex-platform-framework-client-themes-shared-icon-chooser-column.component-Ci_oH7NE.mjs.map → acorex-platform-framework-client-themes-shared-icon-chooser-column.component-CbZALjB5.mjs.map} +1 -1
- package/fesm2022/{acorex-platform-framework-client-themes-shared-icon-chooser-view.component-Lb1G2Xz7.mjs → acorex-platform-framework-client-themes-shared-icon-chooser-view.component-BRrNT6x2.mjs} +8 -6
- package/fesm2022/{acorex-platform-framework-client-themes-shared-icon-chooser-view.component-Lb1G2Xz7.mjs.map → acorex-platform-framework-client-themes-shared-icon-chooser-view.component-BRrNT6x2.mjs.map} +1 -1
- package/fesm2022/{acorex-platform-framework-client-themes-shared-theme-color-chooser-column.component-D4ubvFYf.mjs → acorex-platform-framework-client-themes-shared-theme-color-chooser-column.component-BIsQTr6B.mjs} +4 -4
- package/fesm2022/{acorex-platform-framework-client-themes-shared-theme-color-chooser-column.component-D4ubvFYf.mjs.map → acorex-platform-framework-client-themes-shared-theme-color-chooser-column.component-BIsQTr6B.mjs.map} +1 -1
- package/fesm2022/{acorex-platform-framework-client-themes-shared-theme-color-chooser-view.component-Br8j_BF_.mjs → acorex-platform-framework-client-themes-shared-theme-color-chooser-view.component-ZclmSbt9.mjs} +16 -10
- package/fesm2022/{acorex-platform-framework-client-themes-shared-theme-color-chooser-view.component-Br8j_BF_.mjs.map → acorex-platform-framework-client-themes-shared-theme-color-chooser-view.component-ZclmSbt9.mjs.map} +1 -1
- package/fesm2022/acorex-platform-framework-client-themes-shared.mjs +538 -133
- package/fesm2022/acorex-platform-framework-client-themes-shared.mjs.map +1 -1
- package/fesm2022/acorex-platform-framework-client-workflow.mjs +26 -66
- package/fesm2022/acorex-platform-framework-client-workflow.mjs.map +1 -1
- package/package.json +10 -10
- package/realtime/README.md +16 -0
- package/runtime/README.md +14 -0
- package/types/acorex-platform-framework-client-auth.d.ts +291 -52
- package/types/acorex-platform-framework-client-common.d.ts +613 -87
- package/types/acorex-platform-framework-client-core.d.ts +213 -206
- package/types/acorex-platform-framework-client-layout-builder.d.ts +14 -8
- package/types/acorex-platform-framework-client-layout-components.d.ts +392 -28
- package/types/acorex-platform-framework-client-layout-entity.d.ts +667 -417
- package/types/acorex-platform-framework-client-layout-views.d.ts +31 -11
- package/types/acorex-platform-framework-client-layout-widget-core.d.ts +66 -17
- package/types/acorex-platform-framework-client-layout-widgets.d.ts +294 -67
- package/types/acorex-platform-framework-client-realtime.d.ts +183 -6
- package/types/acorex-platform-framework-client-runtime.d.ts +134 -28
- package/types/acorex-platform-framework-client-themes-default.d.ts +9 -1
- package/types/acorex-platform-framework-client-themes-shared.d.ts +8 -2
- package/types/acorex-platform-framework-client-workflow.d.ts +11 -35
- package/fesm2022/acorex-platform-framework-client-common-common-settings.provider-Euotv9l0.mjs.map +0 -1
- package/fesm2022/acorex-platform-framework-client-layout-entity-attachments-page.component-BzUM-JMC.mjs.map +0 -1
- package/fesm2022/acorex-platform-framework-client-layout-entity-file-list-popup.component-Ci_CTFtq.mjs.map +0 -1
- package/fesm2022/acorex-platform-framework-client-layout-entity-gallery-page.component-DmiB__JR.mjs.map +0 -1
- package/fesm2022/acorex-platform-framework-client-layout-widgets-image-preview.popup-CazpERbX.mjs.map +0 -1
- package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-single-view.component-CIf1VWNW.mjs.map +0 -1
- package/fesm2022/acorex-platform-framework-client-themes-default-error-offline.component-DRz-kVsT.mjs.map +0 -1
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { InjectionToken, Injector, Injectable, signal, inject, Input, Directive,
|
|
3
|
-
import { map, BehaviorSubject, shareReplay, defaultIfEmpty, switchMap, filter, from, combineLatest, first } from 'rxjs';
|
|
4
|
-
import { AXPBroadcastEventService, AXP_SESSION_SERVICE } from '@acorex-platform/framework-client/core';
|
|
2
|
+
import { InjectionToken, Injector, Injectable, signal, inject, Input, Directive, Optional, Inject, NgModule, input, output, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
|
+
import { map, BehaviorSubject, shareReplay, defaultIfEmpty, switchMap, filter, distinctUntilChanged, of, from, combineLatest, first } from 'rxjs';
|
|
4
|
+
import { AXPDebugService, AXPBroadcastEventService, AXP_SESSION_SERVICE, AXP_EXPRESSION_EVALUATOR_SCOPE_PROVIDER, provideAppStartUpTask, AXP_APP_STARTUP_PRIORITY } from '@acorex-platform/framework-client/core';
|
|
5
5
|
import { isEmpty } from 'lodash-es';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { AXPCommandService, AXPQueryService } from '@acorex-platform/framework-client/runtime';
|
|
7
|
+
import { AUTH_LOGOUT_DEFAULT, AUTH_REFRESH_DEFAULT, AUTH_UPDATE_SESSION_DEFAULT, AUTH_LIST_PROVIDERS_DEFAULT } from '@acorex-platform/framework-shared/runtime';
|
|
8
|
+
import { runtimeQueryPayloadData, runtimeCommandPayloadData } from '@acorex-platform/framework-shared/core';
|
|
8
9
|
|
|
9
10
|
const AXP_APPLICATION_LOADER = new InjectionToken('AXP_APPLICATION_LOADER', {
|
|
10
11
|
providedIn: 'root',
|
|
@@ -39,13 +40,6 @@ class AXPApplicationDefaultLoader {
|
|
|
39
40
|
}
|
|
40
41
|
}
|
|
41
42
|
|
|
42
|
-
//#region ---- Datasource keys ----
|
|
43
|
-
/** Platform auth datasource registry names. */
|
|
44
|
-
const AXPPlatformAuthDataSourceKeys = {
|
|
45
|
-
PermissionDefinitions: 'platform-permission-definitions',
|
|
46
|
-
};
|
|
47
|
-
//#endregion
|
|
48
|
-
|
|
49
43
|
class AXPUnauthorizedError extends Error {
|
|
50
44
|
constructor(message, data) {
|
|
51
45
|
super(message);
|
|
@@ -111,6 +105,59 @@ class AXPTenantDefaultLoader {
|
|
|
111
105
|
}
|
|
112
106
|
}
|
|
113
107
|
|
|
108
|
+
/** Debug namespace for auth session diagnostics. Filter console with `[AXP:AUTH-SESSION]`. */
|
|
109
|
+
const AUTH_SESSION_TRACE_NS = 'AUTH-SESSION';
|
|
110
|
+
//#region ----- Trace area titles -----
|
|
111
|
+
const AUTH_SESSION_TRACE_TITLE = {
|
|
112
|
+
/** Sign-in / register / sign-out lifecycle. */
|
|
113
|
+
Lifecycle: 'Lifecycle',
|
|
114
|
+
/** Cookie session restore + refresh. */
|
|
115
|
+
Restore: 'Restore',
|
|
116
|
+
/** Tenant / application bind via UpdateSession. */
|
|
117
|
+
Workspace: 'Workspace',
|
|
118
|
+
/** Auth:GetAccessPolicy load (permissions + features). */
|
|
119
|
+
AccessPolicy: 'AccessPolicy',
|
|
120
|
+
/** ListTenants / ListApplications. */
|
|
121
|
+
Catalog: 'Catalog',
|
|
122
|
+
/** Route guards (login, tenant-chooser, app-chooser). */
|
|
123
|
+
Guard: 'Guard',
|
|
124
|
+
};
|
|
125
|
+
//#endregion
|
|
126
|
+
|
|
127
|
+
//#region ----- Lifecycle -----
|
|
128
|
+
function traceAuthLifecycle(message, data) {
|
|
129
|
+
AXPDebugService.instance?.trace(AUTH_SESSION_TRACE_NS, AUTH_SESSION_TRACE_TITLE.Lifecycle, message, data);
|
|
130
|
+
}
|
|
131
|
+
function traceAuthLifecycleError(message, data) {
|
|
132
|
+
AXPDebugService.instance?.error(AUTH_SESSION_TRACE_NS, AUTH_SESSION_TRACE_TITLE.Lifecycle, message, data);
|
|
133
|
+
}
|
|
134
|
+
//#endregion
|
|
135
|
+
//#region ----- Restore -----
|
|
136
|
+
function traceAuthRestore(message, data) {
|
|
137
|
+
AXPDebugService.instance?.trace(AUTH_SESSION_TRACE_NS, AUTH_SESSION_TRACE_TITLE.Restore, message, data);
|
|
138
|
+
}
|
|
139
|
+
//#endregion
|
|
140
|
+
//#region ----- Workspace -----
|
|
141
|
+
function traceAuthWorkspace(message, data) {
|
|
142
|
+
AXPDebugService.instance?.trace(AUTH_SESSION_TRACE_NS, AUTH_SESSION_TRACE_TITLE.Workspace, message, data);
|
|
143
|
+
}
|
|
144
|
+
//#endregion
|
|
145
|
+
//#region ----- Access policy -----
|
|
146
|
+
function traceAuthAccessPolicy(message, data) {
|
|
147
|
+
AXPDebugService.instance?.trace(AUTH_SESSION_TRACE_NS, AUTH_SESSION_TRACE_TITLE.AccessPolicy, message, data);
|
|
148
|
+
}
|
|
149
|
+
//#endregion
|
|
150
|
+
//#region ----- Catalog -----
|
|
151
|
+
function traceAuthCatalog(message, data) {
|
|
152
|
+
AXPDebugService.instance?.trace(AUTH_SESSION_TRACE_NS, AUTH_SESSION_TRACE_TITLE.Catalog, message, data);
|
|
153
|
+
}
|
|
154
|
+
//#endregion
|
|
155
|
+
//#region ----- Guards -----
|
|
156
|
+
function traceAuthGuard(message, data) {
|
|
157
|
+
AXPDebugService.instance?.trace(AUTH_SESSION_TRACE_NS, AUTH_SESSION_TRACE_TITLE.Guard, message, data);
|
|
158
|
+
}
|
|
159
|
+
//#endregion
|
|
160
|
+
|
|
114
161
|
class AXPAuthStrategyRegistryService {
|
|
115
162
|
constructor(injector) {
|
|
116
163
|
this.strategies = new Map();
|
|
@@ -128,10 +175,10 @@ class AXPAuthStrategyRegistryService {
|
|
|
128
175
|
get(strategyKey) {
|
|
129
176
|
return this.strategies.get(strategyKey);
|
|
130
177
|
}
|
|
131
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
132
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
178
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPAuthStrategyRegistryService, deps: [{ token: i0.Injector }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
179
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPAuthStrategyRegistryService, providedIn: 'root' }); }
|
|
133
180
|
}
|
|
134
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
181
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPAuthStrategyRegistryService, decorators: [{
|
|
135
182
|
type: Injectable,
|
|
136
183
|
args: [{
|
|
137
184
|
providedIn: 'root'
|
|
@@ -154,7 +201,8 @@ class AXPFeatureDirective {
|
|
|
154
201
|
constructor(templateRef, viewContainer) {
|
|
155
202
|
this.templateRef = templateRef;
|
|
156
203
|
this.viewContainer = viewContainer;
|
|
157
|
-
this.hasView = signal(false,
|
|
204
|
+
this.hasView = signal(false, /* @ts-ignore */
|
|
205
|
+
...(ngDevMode ? [{ debugName: "hasView" }] : /* istanbul ignore next */ []));
|
|
158
206
|
this.sessionService = inject(AXPSessionService);
|
|
159
207
|
}
|
|
160
208
|
set feature(featureKeys) {
|
|
@@ -188,10 +236,10 @@ class AXPFeatureDirective {
|
|
|
188
236
|
ngOnDestroy() {
|
|
189
237
|
this.subscription?.unsubscribe();
|
|
190
238
|
}
|
|
191
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
192
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "
|
|
239
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPFeatureDirective, deps: [{ token: i0.TemplateRef }, { token: i0.ViewContainerRef }], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
240
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.8", type: AXPFeatureDirective, isStandalone: false, selector: "[feature]", inputs: { feature: "feature", featureElse: "featureElse" }, ngImport: i0 }); }
|
|
193
241
|
}
|
|
194
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
242
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPFeatureDirective, decorators: [{
|
|
195
243
|
type: Directive,
|
|
196
244
|
args: [{
|
|
197
245
|
selector: '[feature]',
|
|
@@ -225,6 +273,36 @@ const AXPFeatureGuard = (route, state) => {
|
|
|
225
273
|
*/
|
|
226
274
|
const AXP_FEATURE_CHECKER = new InjectionToken('AXP_FEATURE_CHECKER');
|
|
227
275
|
|
|
276
|
+
//#region ----- Imports -----
|
|
277
|
+
//#endregion
|
|
278
|
+
//#region ----- Feature Evaluator Scope Provider -----
|
|
279
|
+
/**
|
|
280
|
+
* Exposes `feature.check(...)` / `feature.checkAny(...)` for entity expressions
|
|
281
|
+
* (e.g. `schema.visible: '{{ feature.check("Module:Feature:X") }}'`).
|
|
282
|
+
*/
|
|
283
|
+
class AXPFeatureEvaluatorScopeProvider {
|
|
284
|
+
constructor() {
|
|
285
|
+
this.sessionService = inject(AXPSessionService);
|
|
286
|
+
}
|
|
287
|
+
async provide(context) {
|
|
288
|
+
context.addScope('feature', {
|
|
289
|
+
/**
|
|
290
|
+
* True when every listed feature key is enabled for the session.
|
|
291
|
+
*/
|
|
292
|
+
check: (...features) => {
|
|
293
|
+
return this.sessionService.isFeatureEnabled(...features);
|
|
294
|
+
},
|
|
295
|
+
/**
|
|
296
|
+
* True when any listed feature key is enabled for the session.
|
|
297
|
+
*/
|
|
298
|
+
checkAny: (...features) => {
|
|
299
|
+
return features.some((f) => this.sessionService.isFeatureEnabled(f));
|
|
300
|
+
},
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
//#endregion
|
|
305
|
+
|
|
228
306
|
const AXP_PERMISSION_LOADER = new InjectionToken('AXP_PERMISSION_LOADER', {
|
|
229
307
|
providedIn: 'root',
|
|
230
308
|
factory: () => {
|
|
@@ -247,6 +325,33 @@ const AXP_PERMISSION_CHECKER = new InjectionToken('AXP_PERMISSION_CHECKER');
|
|
|
247
325
|
const AXP_ACCESS_POLICY_CACHE_PORT = new InjectionToken('AXP_ACCESS_POLICY_CACHE_PORT');
|
|
248
326
|
//#endregion
|
|
249
327
|
|
|
328
|
+
//#region ---- OAuth pending strategy (survives restoreSession) ----
|
|
329
|
+
/**
|
|
330
|
+
* Persisted while an authorization-code redirect is in flight.
|
|
331
|
+
* `restoreSession` clears partial `AXP_SESSION` (no user / no cookies) — this key
|
|
332
|
+
* must survive so `/auth/oauth-callback` can resolve the strategy.
|
|
333
|
+
*/
|
|
334
|
+
const AXP_OAUTH_PENDING_STRATEGY_KEY = 'axp_oauth_pending_strategy';
|
|
335
|
+
/** Set by platform OAuth strategies before redirect (PKCE). */
|
|
336
|
+
const AXP_OAUTH_PKCE_VERIFIER_KEY = 'pkce_code_verifier';
|
|
337
|
+
function setOAuthPendingStrategy(strategy) {
|
|
338
|
+
const trimmed = strategy?.trim();
|
|
339
|
+
if (!trimmed) {
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
localStorage.setItem(AXP_OAUTH_PENDING_STRATEGY_KEY, trimmed);
|
|
343
|
+
}
|
|
344
|
+
function getOAuthPendingStrategy() {
|
|
345
|
+
return localStorage.getItem(AXP_OAUTH_PENDING_STRATEGY_KEY)?.trim() || null;
|
|
346
|
+
}
|
|
347
|
+
function clearOAuthPendingStrategy() {
|
|
348
|
+
localStorage.removeItem(AXP_OAUTH_PENDING_STRATEGY_KEY);
|
|
349
|
+
}
|
|
350
|
+
function isOAuthRedirectInFlight() {
|
|
351
|
+
return !!localStorage.getItem(AXP_OAUTH_PKCE_VERIFIER_KEY) || !!getOAuthPendingStrategy();
|
|
352
|
+
}
|
|
353
|
+
//#endregion
|
|
354
|
+
|
|
250
355
|
class AXPSessionService {
|
|
251
356
|
constructor() {
|
|
252
357
|
this.eventService = inject(AXPBroadcastEventService);
|
|
@@ -259,6 +364,9 @@ class AXPSessionService {
|
|
|
259
364
|
this.permissionChecker = inject(AXP_PERMISSION_CHECKER, { optional: true });
|
|
260
365
|
this.featureChecker = inject(AXP_FEATURE_CHECKER, { optional: true });
|
|
261
366
|
this.accessPolicyCache = inject(AXP_ACCESS_POLICY_CACHE_PORT, { optional: true });
|
|
367
|
+
/** In-memory catalog caches — shared by guards and chooser UI (one List* per bind). */
|
|
368
|
+
this.tenantsListCache = null;
|
|
369
|
+
this.applicationsListCache = null;
|
|
262
370
|
this.status = new BehaviorSubject(AXPSessionStatus.Unauthenticated);
|
|
263
371
|
this.status$ = this.status.asObservable().pipe(shareReplay(1));
|
|
264
372
|
// Add loading state to prevent premature redirects
|
|
@@ -274,10 +382,7 @@ class AXPSessionService {
|
|
|
274
382
|
this.permissions$ = this.permissionsSubject.asObservable().pipe(shareReplay(1), defaultIfEmpty([]));
|
|
275
383
|
this.featuresSubject = new BehaviorSubject([]);
|
|
276
384
|
this.features$ = this.featuresSubject.asObservable().pipe(shareReplay(1), defaultIfEmpty([]));
|
|
277
|
-
this.isAuthenticated$ = this.status$.pipe(map((status) =>
|
|
278
|
-
const isAuth = status === AXPSessionStatus.Authenticated || status === AXPSessionStatus.Authorized;
|
|
279
|
-
return isAuth;
|
|
280
|
-
}), shareReplay(1));
|
|
385
|
+
this.isAuthenticated$ = this.status$.pipe(map((status) => this.isAuthenticatedStatus(status)), shareReplay(1));
|
|
281
386
|
// Add a new observable that considers loading state
|
|
282
387
|
this.isAuthenticatedWithLoading$ = this.isLoading$.pipe(switchMap((loading) => {
|
|
283
388
|
if (loading) {
|
|
@@ -288,6 +393,24 @@ class AXPSessionService {
|
|
|
288
393
|
return this.isAuthenticated$;
|
|
289
394
|
}), shareReplay(1));
|
|
290
395
|
this.isAuthorized$ = this.status$.pipe(map((status) => status === AXPSessionStatus.Authorized), shareReplay(1));
|
|
396
|
+
this.isReadyForAppScopedApi$ = this.isAuthorized$.pipe(map((authorized) => authorized && this.hasSessionCredentials()), distinctUntilChanged(), shareReplay(1));
|
|
397
|
+
/** Tenant list for the signed-in user; skips the API when there is no cookie session. */
|
|
398
|
+
this.tenants$ = this.isAuthenticated$.pipe(distinctUntilChanged(), switchMap((authenticated) => {
|
|
399
|
+
if (!authenticated || !this.hasSessionCredentials()) {
|
|
400
|
+
return of([]);
|
|
401
|
+
}
|
|
402
|
+
return from(this.getTenantsList());
|
|
403
|
+
}), shareReplay({ bufferSize: 1, refCount: true }));
|
|
404
|
+
/** Application list for the current tenant; skips the API when there is no cookie session. */
|
|
405
|
+
this.applications$ = combineLatest([
|
|
406
|
+
this.isAuthenticated$,
|
|
407
|
+
this.tenant$,
|
|
408
|
+
]).pipe(distinctUntilChanged(([authenticated, tenant], [previousAuthenticated, previousTenant]) => authenticated === previousAuthenticated && tenant?.id === previousTenant?.id), switchMap(([authenticated, tenant]) => {
|
|
409
|
+
if (!authenticated || !this.hasSessionCredentials() || !tenant?.id) {
|
|
410
|
+
return of([]);
|
|
411
|
+
}
|
|
412
|
+
return from(this.getApplicationsList());
|
|
413
|
+
}), shareReplay({ bufferSize: 1, refCount: true }));
|
|
291
414
|
/** Waits for session restore to finish, then emits whether the session is fully authorized. */
|
|
292
415
|
this.isAuthorizedWithLoading$ = this.isLoading$.pipe(switchMap((loading) => {
|
|
293
416
|
if (loading) {
|
|
@@ -305,48 +428,109 @@ class AXPSessionService {
|
|
|
305
428
|
this.syncContextSubjectsFromStorage();
|
|
306
429
|
return this.currentTenantSubject.value;
|
|
307
430
|
}
|
|
308
|
-
get tenants$() {
|
|
309
|
-
return from(this.tenantLoader.getList(this.getContext()));
|
|
310
|
-
}
|
|
311
431
|
get application() {
|
|
312
432
|
this.syncContextSubjectsFromStorage();
|
|
313
433
|
return this.currentApplicationSubject.value;
|
|
314
434
|
}
|
|
315
|
-
get applications$() {
|
|
316
|
-
return from(this.applicationLoader.getList(this.getContext()));
|
|
317
|
-
}
|
|
318
435
|
get permissions() {
|
|
319
436
|
return this.permissionsSubject.value ?? [];
|
|
320
437
|
}
|
|
321
438
|
get features() {
|
|
322
439
|
return this.featuresSubject.value ?? [];
|
|
323
440
|
}
|
|
441
|
+
/** Synchronous counterpart to {@link isAuthenticated$}. */
|
|
442
|
+
get isAuthenticated() {
|
|
443
|
+
return this.isAuthenticatedStatus(this.status.value);
|
|
444
|
+
}
|
|
445
|
+
/** Synchronous counterpart to {@link isAuthorized$}. */
|
|
446
|
+
get isAuthorized() {
|
|
447
|
+
return this.status.value === AXPSessionStatus.Authorized;
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Tenant and application are selected and permissions are loaded.
|
|
451
|
+
* Gate app-scoped runtime work (entity queries, inbox, activity).
|
|
452
|
+
*/
|
|
453
|
+
get isReadyForAppScopedApi() {
|
|
454
|
+
return this.isAuthorized && this.hasSessionCredentials();
|
|
455
|
+
}
|
|
324
456
|
async restoreSession() {
|
|
325
457
|
this.isLoading.next(true);
|
|
458
|
+
const startedAt = Date.now();
|
|
326
459
|
try {
|
|
327
460
|
const sessionData = this.getSessionData();
|
|
328
|
-
if (sessionData) {
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
461
|
+
if (!sessionData?.user || !sessionData.cookieSession) {
|
|
462
|
+
// OAuth code exchange needs `strategy` after redirect. Do not wipe the
|
|
463
|
+
// pre-login strategy / pending key while PKCE (or pending strategy) exists.
|
|
464
|
+
if (isOAuthRedirectInFlight()) {
|
|
465
|
+
const strategy = sessionData?.strategy || getOAuthPendingStrategy();
|
|
466
|
+
if (strategy && sessionData?.strategy !== strategy) {
|
|
467
|
+
this.setSession({
|
|
468
|
+
strategy,
|
|
469
|
+
user: null,
|
|
470
|
+
tenant: null,
|
|
471
|
+
application: null,
|
|
472
|
+
cookieSession: false,
|
|
473
|
+
});
|
|
337
474
|
}
|
|
338
|
-
this.status.next(AXPSessionStatus.
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
await this.syncSessionAuthorization();
|
|
475
|
+
this.status.next(AXPSessionStatus.Unauthorized);
|
|
476
|
+
traceAuthRestore('OAuth redirect in flight — kept strategy', () => ({ strategy }));
|
|
477
|
+
return;
|
|
342
478
|
}
|
|
479
|
+
this.clearSession();
|
|
480
|
+
this.status.next(AXPSessionStatus.Unauthorized);
|
|
481
|
+
traceAuthRestore('No cookie session metadata — cleared');
|
|
482
|
+
return;
|
|
343
483
|
}
|
|
344
|
-
|
|
484
|
+
// Validate HttpOnly cookies and rotate tokens before trusting local metadata.
|
|
485
|
+
const strategyName = sessionData.strategy;
|
|
486
|
+
const strategy = strategyName ? this.authStrategyRegistry.get(strategyName) : null;
|
|
487
|
+
if (!strategy) {
|
|
488
|
+
this.clearSession();
|
|
489
|
+
this.status.next(AXPSessionStatus.Unauthorized);
|
|
490
|
+
traceAuthRestore('Strategy missing — cleared', () => ({ strategyName }));
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
// Keep subjects warm for refresh context, but do NOT mark Authenticated yet —
|
|
494
|
+
// that would trigger settings/chat side-effects while isLoading is still true.
|
|
495
|
+
this.currentUserSubject.next(sessionData.user);
|
|
496
|
+
this.currentTenantSubject.next(sessionData.tenant ?? null);
|
|
497
|
+
this.currentApplicationSubject.next(sessionData.application ?? null);
|
|
498
|
+
traceAuthRestore('Refreshing cookie session', () => ({
|
|
499
|
+
userId: sessionData.user?.id,
|
|
500
|
+
tenantId: sessionData.tenant?.id,
|
|
501
|
+
applicationId: sessionData.application?.id,
|
|
502
|
+
strategy: strategyName,
|
|
503
|
+
}));
|
|
504
|
+
const refreshResult = await strategy.refreshToken(this.getContext());
|
|
505
|
+
if (!refreshResult?.succeed || !refreshResult.data?.user) {
|
|
506
|
+
this.clearSession();
|
|
345
507
|
this.status.next(AXPSessionStatus.Unauthorized);
|
|
508
|
+
traceAuthRestore('Refresh failed — cleared');
|
|
509
|
+
return;
|
|
346
510
|
}
|
|
511
|
+
// Preserve workspace binding from local metadata when refresh omits it.
|
|
512
|
+
const merged = {
|
|
513
|
+
succeed: true,
|
|
514
|
+
data: {
|
|
515
|
+
...refreshResult.data,
|
|
516
|
+
tenant: refreshResult.data.tenant ?? sessionData.tenant ?? null,
|
|
517
|
+
application: refreshResult.data.application ?? sessionData.application ?? null,
|
|
518
|
+
},
|
|
519
|
+
};
|
|
520
|
+
await this.applySignInResult(strategyName, merged);
|
|
521
|
+
traceAuthRestore('Restore complete', () => ({
|
|
522
|
+
ms: Date.now() - startedAt,
|
|
523
|
+
status: this.status.value,
|
|
524
|
+
tenantId: this.tenant?.id,
|
|
525
|
+
applicationId: this.application?.id,
|
|
526
|
+
}));
|
|
347
527
|
}
|
|
348
528
|
catch (error) {
|
|
529
|
+
this.clearSession();
|
|
349
530
|
this.status.next(AXPSessionStatus.Unauthorized);
|
|
531
|
+
traceAuthRestore('Restore error — cleared', () => ({
|
|
532
|
+
error: error instanceof Error ? error.message : String(error),
|
|
533
|
+
}));
|
|
350
534
|
}
|
|
351
535
|
finally {
|
|
352
536
|
this.isLoading.next(false);
|
|
@@ -354,33 +538,45 @@ class AXPSessionService {
|
|
|
354
538
|
}
|
|
355
539
|
async signin(credentials) {
|
|
356
540
|
this.isLoading.next(true);
|
|
541
|
+
const startedAt = Date.now();
|
|
542
|
+
traceAuthLifecycle('Sign-in started', () => ({ strategy: credentials.strategy }));
|
|
357
543
|
try {
|
|
358
|
-
//
|
|
359
|
-
// this.clearSession();
|
|
360
|
-
//
|
|
361
544
|
const strategy = this.authStrategyRegistry.get(credentials.strategy);
|
|
362
545
|
if (!strategy) {
|
|
363
546
|
throw new Error(`Authentication strategy '${credentials.strategy}' is not supported`);
|
|
364
547
|
}
|
|
365
548
|
const result = await strategy.signin(credentials);
|
|
366
|
-
if (!result)
|
|
549
|
+
if (!result) {
|
|
550
|
+
// Redirect / multi-step strategies return void — keep strategy + PKCE metadata.
|
|
551
|
+
this.status.next(AXPSessionStatus.Unauthenticated);
|
|
367
552
|
return;
|
|
553
|
+
}
|
|
368
554
|
if (result?.succeed) {
|
|
369
555
|
await this.applySignInResult(credentials.strategy, result);
|
|
556
|
+
traceAuthLifecycle('Sign-in succeeded', () => ({
|
|
557
|
+
ms: Date.now() - startedAt,
|
|
558
|
+
userId: this.user?.id,
|
|
559
|
+
tenantId: this.tenant?.id,
|
|
560
|
+
applicationId: this.application?.id,
|
|
561
|
+
status: this.status.value,
|
|
562
|
+
}));
|
|
370
563
|
}
|
|
371
564
|
else {
|
|
565
|
+
this.clearSession();
|
|
372
566
|
this.status.next(AXPSessionStatus.Unauthenticated);
|
|
373
567
|
throw new Error(`Invalid Username or Password`);
|
|
374
568
|
}
|
|
375
569
|
}
|
|
376
570
|
catch (error) {
|
|
377
|
-
|
|
571
|
+
traceAuthLifecycleError('Sign-in failed', () => ({
|
|
572
|
+
error: error instanceof Error ? error.message : String(error),
|
|
573
|
+
}));
|
|
574
|
+
this.clearSession();
|
|
378
575
|
this.status.next(AXPSessionStatus.Unauthenticated);
|
|
379
576
|
throw error;
|
|
380
577
|
}
|
|
381
578
|
finally {
|
|
382
579
|
this.isLoading.next(false);
|
|
383
|
-
console.log('Signin process completed');
|
|
384
580
|
}
|
|
385
581
|
}
|
|
386
582
|
async register(credentials) {
|
|
@@ -399,6 +595,7 @@ class AXPSessionService {
|
|
|
399
595
|
}
|
|
400
596
|
catch (error) {
|
|
401
597
|
console.error('Register error:', error);
|
|
598
|
+
this.clearSession();
|
|
402
599
|
this.status.next(AXPSessionStatus.Unauthenticated);
|
|
403
600
|
throw error;
|
|
404
601
|
}
|
|
@@ -407,38 +604,72 @@ class AXPSessionService {
|
|
|
407
604
|
}
|
|
408
605
|
}
|
|
409
606
|
async applySignInResult(strategy, result) {
|
|
410
|
-
|
|
607
|
+
// Reset client metadata only — do not call clearSession() (that flips status to
|
|
608
|
+
// Unauthenticated and can trigger logout listeners mid-flow).
|
|
609
|
+
this.invalidateAccessPolicyCache();
|
|
610
|
+
this.clearCatalogCaches();
|
|
611
|
+
this.permissionsSubject.next([]);
|
|
612
|
+
this.featuresSubject.next([]);
|
|
411
613
|
this.currentUserSubject.next(result.data.user);
|
|
412
614
|
this.setSession({
|
|
413
|
-
accessToken: result.data.accessToken,
|
|
414
|
-
refreshToken: result.data.refreshToken,
|
|
415
615
|
strategy,
|
|
416
616
|
user: result.data.user,
|
|
417
617
|
tenant: result.data?.tenant ?? null,
|
|
618
|
+
// Fresh sign-in tokens omit application; refresh/restore may include it.
|
|
418
619
|
application: result.data?.application ?? null,
|
|
419
620
|
expiresIn: result.data?.expiresIn,
|
|
420
|
-
idToken: result.data?.idToken ?? null,
|
|
421
621
|
locked: false,
|
|
622
|
+
cookieSession: true,
|
|
422
623
|
});
|
|
423
|
-
await this.
|
|
424
|
-
await this.loadFeatures();
|
|
624
|
+
await this.reloadAccessPolicyIfReady('applySignInResult');
|
|
425
625
|
await this.syncSessionAuthorization();
|
|
426
626
|
}
|
|
627
|
+
/**
|
|
628
|
+
* Clears the selected application so the user must pick one (app chooser).
|
|
629
|
+
* Keeps tenant/user/token intact and demotes status to Authenticated.
|
|
630
|
+
*/
|
|
631
|
+
clearApplicationSelection() {
|
|
632
|
+
if (!this.application?.id) {
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
this.setSession({ application: null });
|
|
636
|
+
if (this.user) {
|
|
637
|
+
this.status.next(AXPSessionStatus.Authenticated);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
427
640
|
async updateToken(params) {
|
|
428
641
|
this.isLoading.next(true);
|
|
642
|
+
const startedAt = Date.now();
|
|
643
|
+
const isApplicationUpdate = this.isApplicationContextUpdate(params);
|
|
644
|
+
const isTenantUpdate = this.isTenantContextUpdate(params);
|
|
645
|
+
traceAuthWorkspace('UpdateSession started', () => ({
|
|
646
|
+
isTenantUpdate,
|
|
647
|
+
isApplicationUpdate,
|
|
648
|
+
tenantId: params?.['tenant']?.id ?? params?.['tenantId'],
|
|
649
|
+
applicationId: params?.['application']?.id ?? params?.['applicationId'],
|
|
650
|
+
}));
|
|
651
|
+
// Pause app-scoped API work while token/context is rebound (avoids 401/403 races).
|
|
652
|
+
if (this.isAuthorized) {
|
|
653
|
+
this.status.next(AXPSessionStatus.Authenticated);
|
|
654
|
+
}
|
|
655
|
+
if (isTenantUpdate && !isApplicationUpdate) {
|
|
656
|
+
this.currentApplicationSubject.next(null);
|
|
657
|
+
this.applicationsListCache = null;
|
|
658
|
+
}
|
|
429
659
|
try {
|
|
430
|
-
const strategyName = this.
|
|
660
|
+
const strategyName = this.resolveStrategyName();
|
|
431
661
|
if (!strategyName) {
|
|
432
662
|
throw new Error('Strategy not found');
|
|
433
663
|
}
|
|
434
664
|
const strategy = this.authStrategyRegistry.get(strategyName);
|
|
435
665
|
if (!strategy) {
|
|
436
|
-
throw new Error(`Authentication strategy '${
|
|
666
|
+
throw new Error(`Authentication strategy '${strategyName}' is not supported`);
|
|
437
667
|
}
|
|
438
668
|
const result = await strategy.updateToken(params);
|
|
439
669
|
if (result?.succeed) {
|
|
440
670
|
if (params?.['code']) {
|
|
441
671
|
this.clearSession();
|
|
672
|
+
clearOAuthPendingStrategy();
|
|
442
673
|
}
|
|
443
674
|
else {
|
|
444
675
|
this.permissionsSubject.next([]);
|
|
@@ -447,19 +678,22 @@ class AXPSessionService {
|
|
|
447
678
|
}
|
|
448
679
|
this.currentUserSubject.next(result.data.user);
|
|
449
680
|
this.setSession({
|
|
450
|
-
accessToken: result.data.accessToken,
|
|
451
|
-
refreshToken: result.data.refreshToken,
|
|
452
681
|
strategy: strategyName,
|
|
453
682
|
user: result.data.user,
|
|
454
683
|
tenant: result.data?.tenant ?? null,
|
|
455
|
-
application: result.data?.application ?? null,
|
|
684
|
+
application: this.resolveApplicationAfterUpdate(result.data?.application ?? null, isApplicationUpdate, isTenantUpdate),
|
|
456
685
|
expiresIn: result.data?.expiresIn,
|
|
457
|
-
idToken: result.data?.idToken ?? null,
|
|
458
686
|
locked: false,
|
|
687
|
+
cookieSession: true,
|
|
459
688
|
});
|
|
460
|
-
await this.
|
|
461
|
-
await this.loadFeatures();
|
|
689
|
+
await this.reloadAccessPolicyIfReady('updateToken');
|
|
462
690
|
await this.syncSessionAuthorization();
|
|
691
|
+
traceAuthWorkspace('UpdateSession complete', () => ({
|
|
692
|
+
ms: Date.now() - startedAt,
|
|
693
|
+
status: this.status.value,
|
|
694
|
+
tenantId: this.tenant?.id,
|
|
695
|
+
applicationId: this.application?.id,
|
|
696
|
+
}));
|
|
463
697
|
}
|
|
464
698
|
else {
|
|
465
699
|
this.status.next(AXPSessionStatus.Unauthenticated);
|
|
@@ -467,16 +701,23 @@ class AXPSessionService {
|
|
|
467
701
|
}
|
|
468
702
|
}
|
|
469
703
|
catch (error) {
|
|
470
|
-
|
|
704
|
+
traceAuthLifecycleError('UpdateSession failed', () => ({
|
|
705
|
+
error: error instanceof Error ? error.message : String(error),
|
|
706
|
+
}));
|
|
707
|
+
// Failed workspace bind must not leave a half-authenticated cookie session
|
|
708
|
+
// (that causes splash restore loops after refresh).
|
|
709
|
+
if (params?.['code']) {
|
|
710
|
+
clearOAuthPendingStrategy();
|
|
711
|
+
}
|
|
712
|
+
this.clearSession();
|
|
471
713
|
throw error;
|
|
472
714
|
}
|
|
473
715
|
finally {
|
|
474
716
|
this.isLoading.next(false);
|
|
475
|
-
console.log('Update token process completed');
|
|
476
717
|
}
|
|
477
718
|
}
|
|
478
719
|
async signout() {
|
|
479
|
-
|
|
720
|
+
traceAuthLifecycle('Sign-out started', () => ({ userId: this.user?.id }));
|
|
480
721
|
this.isLoading.next(true);
|
|
481
722
|
try {
|
|
482
723
|
const sessionData = this.getSessionData();
|
|
@@ -489,53 +730,76 @@ class AXPSessionService {
|
|
|
489
730
|
catch (error) { }
|
|
490
731
|
}
|
|
491
732
|
}
|
|
492
|
-
//
|
|
493
733
|
const userId = this.user?.id;
|
|
494
734
|
this.clearSession();
|
|
495
735
|
this.eventService.publish(AXPSessionStatus.SignedOut, { id: userId });
|
|
496
736
|
this.isLoading.next(false);
|
|
497
737
|
this.status.next(AXPSessionStatus.SignedOut);
|
|
738
|
+
traceAuthLifecycle('Sign-out complete', () => ({ userId }));
|
|
498
739
|
}
|
|
499
740
|
finally {
|
|
500
741
|
this.isLoading.next(false);
|
|
501
742
|
}
|
|
502
743
|
}
|
|
503
744
|
async refreshToken() {
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
reject(new Error(`Authentication strategy '${sessionData.strategy}' is not found`));
|
|
516
|
-
return;
|
|
517
|
-
}
|
|
518
|
-
try {
|
|
519
|
-
const result = await strategy.refreshToken(this.getContext());
|
|
520
|
-
if (result.succeed) {
|
|
521
|
-
console.log('Token refresh successful');
|
|
522
|
-
this.setSession(result.data);
|
|
523
|
-
resolve(result.data?.accessToken);
|
|
524
|
-
}
|
|
525
|
-
else {
|
|
526
|
-
console.error('Token refresh failed');
|
|
527
|
-
this.clearSession();
|
|
528
|
-
this.status.next(AXPSessionStatus.Expired);
|
|
529
|
-
reject(new Error('Token refresh failed'));
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
catch (error) {
|
|
533
|
-
console.error('Error during token refresh:', error);
|
|
745
|
+
const sessionData = this.getSessionData();
|
|
746
|
+
if (!sessionData?.strategy || !sessionData.cookieSession) {
|
|
747
|
+
throw new Error('No cookie session available');
|
|
748
|
+
}
|
|
749
|
+
const strategy = this.authStrategyRegistry.get(sessionData.strategy);
|
|
750
|
+
if (!strategy) {
|
|
751
|
+
throw new Error(`Authentication strategy '${sessionData.strategy}' is not found`);
|
|
752
|
+
}
|
|
753
|
+
try {
|
|
754
|
+
const result = await strategy.refreshToken(this.getContext());
|
|
755
|
+
if (!result.succeed || !result.data?.user) {
|
|
534
756
|
this.clearSession();
|
|
535
757
|
this.status.next(AXPSessionStatus.Expired);
|
|
536
|
-
|
|
758
|
+
throw new Error('Token refresh failed');
|
|
537
759
|
}
|
|
538
|
-
|
|
760
|
+
this.setSession({
|
|
761
|
+
strategy: sessionData.strategy,
|
|
762
|
+
user: result.data.user,
|
|
763
|
+
tenant: result.data.tenant ?? sessionData.tenant ?? null,
|
|
764
|
+
application: result.data.application ?? sessionData.application ?? null,
|
|
765
|
+
expiresIn: result.data.expiresIn,
|
|
766
|
+
cookieSession: true,
|
|
767
|
+
locked: sessionData.locked === true,
|
|
768
|
+
});
|
|
769
|
+
return true;
|
|
770
|
+
}
|
|
771
|
+
catch (error) {
|
|
772
|
+
this.clearSession();
|
|
773
|
+
this.status.next(AXPSessionStatus.Expired);
|
|
774
|
+
throw error;
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
/**
|
|
778
|
+
* Auth:GetAccessPolicy is only meaningful with tenant + application bound.
|
|
779
|
+
* Skipping incomplete contexts cuts 2 redundant calls on the common login path.
|
|
780
|
+
*/
|
|
781
|
+
async reloadAccessPolicyIfReady(reason) {
|
|
782
|
+
if (!this.hasCompleteSessionContext()) {
|
|
783
|
+
this.permissionsSubject.next([]);
|
|
784
|
+
this.featuresSubject.next([]);
|
|
785
|
+
traceAuthAccessPolicy('Skipped GetAccessPolicy (incomplete workspace)', () => ({
|
|
786
|
+
reason,
|
|
787
|
+
tenantId: this.tenant?.id ?? null,
|
|
788
|
+
applicationId: this.application?.id ?? null,
|
|
789
|
+
}));
|
|
790
|
+
return;
|
|
791
|
+
}
|
|
792
|
+
const startedAt = Date.now();
|
|
793
|
+
await this.loadPermissions();
|
|
794
|
+
await this.loadFeatures();
|
|
795
|
+
traceAuthAccessPolicy('GetAccessPolicy loaded', () => ({
|
|
796
|
+
reason,
|
|
797
|
+
ms: Date.now() - startedAt,
|
|
798
|
+
permissionCount: this.permissions.length,
|
|
799
|
+
featureCount: this.features.length,
|
|
800
|
+
tenantId: this.tenant?.id,
|
|
801
|
+
applicationId: this.application?.id,
|
|
802
|
+
}));
|
|
539
803
|
}
|
|
540
804
|
async loadPermissions() {
|
|
541
805
|
try {
|
|
@@ -543,7 +807,9 @@ class AXPSessionService {
|
|
|
543
807
|
this.permissionsSubject.next(permissions ?? []);
|
|
544
808
|
}
|
|
545
809
|
catch (error) {
|
|
546
|
-
|
|
810
|
+
traceAuthLifecycleError('Load permissions failed', () => ({
|
|
811
|
+
error: error instanceof Error ? error.message : String(error),
|
|
812
|
+
}));
|
|
547
813
|
this.permissionsSubject.next([]);
|
|
548
814
|
}
|
|
549
815
|
}
|
|
@@ -553,10 +819,62 @@ class AXPSessionService {
|
|
|
553
819
|
this.featuresSubject.next(features ?? []);
|
|
554
820
|
}
|
|
555
821
|
catch (error) {
|
|
556
|
-
|
|
822
|
+
traceAuthLifecycleError('Load features failed', () => ({
|
|
823
|
+
error: error instanceof Error ? error.message : String(error),
|
|
824
|
+
}));
|
|
557
825
|
this.featuresSubject.next([]);
|
|
558
826
|
}
|
|
559
827
|
}
|
|
828
|
+
/** Shared ListTenants — used by guards and {@link tenants$}. */
|
|
829
|
+
async getTenantsList() {
|
|
830
|
+
if (!this.hasSessionCredentials()) {
|
|
831
|
+
return [];
|
|
832
|
+
}
|
|
833
|
+
const userId = this.user?.id ?? '';
|
|
834
|
+
if (this.tenantsListCache?.userId === userId) {
|
|
835
|
+
traceAuthCatalog('Tenants cache hit', () => ({
|
|
836
|
+
userId,
|
|
837
|
+
count: this.tenantsListCache.items.length,
|
|
838
|
+
}));
|
|
839
|
+
return this.tenantsListCache.items;
|
|
840
|
+
}
|
|
841
|
+
const startedAt = Date.now();
|
|
842
|
+
const items = await this.tenantLoader.getList(this.getContext());
|
|
843
|
+
this.tenantsListCache = { userId, items };
|
|
844
|
+
traceAuthCatalog('Tenants fetched', () => ({
|
|
845
|
+
userId,
|
|
846
|
+
count: items.length,
|
|
847
|
+
ms: Date.now() - startedAt,
|
|
848
|
+
}));
|
|
849
|
+
return items;
|
|
850
|
+
}
|
|
851
|
+
/** Shared ListApplications — used by guards and {@link applications$}. */
|
|
852
|
+
async getApplicationsList() {
|
|
853
|
+
if (!this.hasSessionCredentials() || !this.tenant?.id) {
|
|
854
|
+
return [];
|
|
855
|
+
}
|
|
856
|
+
const tenantId = this.tenant.id;
|
|
857
|
+
if (this.applicationsListCache?.tenantId === tenantId) {
|
|
858
|
+
traceAuthCatalog('Applications cache hit', () => ({
|
|
859
|
+
tenantId,
|
|
860
|
+
count: this.applicationsListCache.items.length,
|
|
861
|
+
}));
|
|
862
|
+
return this.applicationsListCache.items;
|
|
863
|
+
}
|
|
864
|
+
const startedAt = Date.now();
|
|
865
|
+
const items = await this.applicationLoader.getList(this.getContext());
|
|
866
|
+
this.applicationsListCache = { tenantId, items };
|
|
867
|
+
traceAuthCatalog('Applications fetched', () => ({
|
|
868
|
+
tenantId,
|
|
869
|
+
count: items.length,
|
|
870
|
+
ms: Date.now() - startedAt,
|
|
871
|
+
}));
|
|
872
|
+
return items;
|
|
873
|
+
}
|
|
874
|
+
clearCatalogCaches() {
|
|
875
|
+
this.tenantsListCache = null;
|
|
876
|
+
this.applicationsListCache = null;
|
|
877
|
+
}
|
|
560
878
|
async signInComplete() {
|
|
561
879
|
// Ensure we have the required data
|
|
562
880
|
if (!this.user) {
|
|
@@ -585,21 +903,25 @@ class AXPSessionService {
|
|
|
585
903
|
}
|
|
586
904
|
this.status.next(AXPSessionStatus.Authenticated);
|
|
587
905
|
}
|
|
588
|
-
setSession(
|
|
589
|
-
const existing = this.getSessionData() ?? {};
|
|
906
|
+
setSession(partial) {
|
|
907
|
+
const existing = this.getSessionData() ?? { strategy: partial.strategy ?? 'user-pass' };
|
|
590
908
|
const sessionData = {
|
|
591
909
|
...existing,
|
|
592
|
-
...
|
|
910
|
+
...partial,
|
|
911
|
+
strategy: partial.strategy ?? existing.strategy ?? 'user-pass',
|
|
593
912
|
};
|
|
594
|
-
//
|
|
595
|
-
|
|
596
|
-
|
|
913
|
+
// Never persist tokens in browser storage — HttpOnly cookies are authoritative.
|
|
914
|
+
delete sessionData.accessToken;
|
|
915
|
+
delete sessionData.refreshToken;
|
|
916
|
+
delete sessionData.idToken;
|
|
917
|
+
if (partial.user) {
|
|
918
|
+
this.currentUserSubject.next(partial.user);
|
|
597
919
|
}
|
|
598
|
-
if ('tenant' in
|
|
599
|
-
this.currentTenantSubject.next(
|
|
920
|
+
if ('tenant' in partial) {
|
|
921
|
+
this.currentTenantSubject.next(partial.tenant ?? null);
|
|
600
922
|
}
|
|
601
|
-
if ('application' in
|
|
602
|
-
this.currentApplicationSubject.next(
|
|
923
|
+
if ('application' in partial) {
|
|
924
|
+
this.currentApplicationSubject.next(partial.application ?? null);
|
|
603
925
|
}
|
|
604
926
|
localStorage.setItem(AXPSessionService.SESSION_KEY, JSON.stringify(sessionData));
|
|
605
927
|
}
|
|
@@ -616,9 +938,19 @@ class AXPSessionService {
|
|
|
616
938
|
this.setSession({ locked });
|
|
617
939
|
}
|
|
618
940
|
setStrategy(strategy) {
|
|
941
|
+
setOAuthPendingStrategy(strategy);
|
|
619
942
|
const sessionData = this.getSessionData();
|
|
620
|
-
|
|
621
|
-
|
|
943
|
+
// Do not invent a persisted session from strategy alone (pre-login).
|
|
944
|
+
// That leaves a partial AXP_SESSION and can break splash restore after a failed sign-in.
|
|
945
|
+
if (!sessionData?.user && !sessionData?.cookieSession) {
|
|
946
|
+
this.setSession({ strategy, user: null, tenant: null, application: null, cookieSession: false });
|
|
947
|
+
return;
|
|
948
|
+
}
|
|
949
|
+
this.setSession({ ...sessionData, strategy });
|
|
950
|
+
}
|
|
951
|
+
/** Session strategy, or pending OAuth strategy left for the callback page. */
|
|
952
|
+
resolveStrategyName() {
|
|
953
|
+
return this.getSessionData()?.strategy?.trim() || getOAuthPendingStrategy();
|
|
622
954
|
}
|
|
623
955
|
getSessionData() {
|
|
624
956
|
try {
|
|
@@ -638,13 +970,15 @@ class AXPSessionService {
|
|
|
638
970
|
}
|
|
639
971
|
clearSession() {
|
|
640
972
|
this.invalidateAccessPolicyCache();
|
|
973
|
+
this.clearCatalogCaches();
|
|
974
|
+
this.status.next(AXPSessionStatus.Unauthenticated);
|
|
641
975
|
this.currentUserSubject.next(null);
|
|
642
976
|
this.currentTenantSubject.next(null);
|
|
643
977
|
this.currentApplicationSubject.next(null);
|
|
644
978
|
this.permissionsSubject.next([]);
|
|
645
979
|
this.featuresSubject.next([]);
|
|
646
980
|
localStorage.removeItem(AXPSessionService.SESSION_KEY);
|
|
647
|
-
|
|
981
|
+
clearOAuthPendingStrategy();
|
|
648
982
|
}
|
|
649
983
|
/** Keeps in-memory subjects aligned with persisted session (e.g. after user switch). */
|
|
650
984
|
syncContextSubjectsFromStorage() {
|
|
@@ -711,9 +1045,16 @@ class AXPSessionService {
|
|
|
711
1045
|
// Module names are stored as features with value: true when enabled
|
|
712
1046
|
return this.features.some(f => f.name === normalizedModuleName && f.value === true);
|
|
713
1047
|
}
|
|
1048
|
+
/**
|
|
1049
|
+
* Access tokens are HttpOnly and not readable from JS.
|
|
1050
|
+
* Use {@link hasSessionCredentials} for auth gating.
|
|
1051
|
+
*/
|
|
714
1052
|
getToken() {
|
|
715
|
-
|
|
716
|
-
|
|
1053
|
+
return undefined;
|
|
1054
|
+
}
|
|
1055
|
+
/** True when the SPA has an active cookie-backed session. */
|
|
1056
|
+
hasSessionCredentials() {
|
|
1057
|
+
return this.getSessionData()?.cookieSession === true && this.isAuthenticated;
|
|
717
1058
|
}
|
|
718
1059
|
getContext() {
|
|
719
1060
|
return new AXPSessionContext({
|
|
@@ -725,10 +1066,28 @@ class AXPSessionService {
|
|
|
725
1066
|
invalidateAccessPolicyCache() {
|
|
726
1067
|
this.accessPolicyCache?.invalidate();
|
|
727
1068
|
}
|
|
728
|
-
|
|
729
|
-
|
|
1069
|
+
isApplicationContextUpdate(params) {
|
|
1070
|
+
return params?.['application'] != null || params?.['applicationId'] != null;
|
|
1071
|
+
}
|
|
1072
|
+
isTenantContextUpdate(params) {
|
|
1073
|
+
return params?.['tenant'] != null || params?.['tenantId'] != null;
|
|
1074
|
+
}
|
|
1075
|
+
resolveApplicationAfterUpdate(fromToken, isApplicationUpdate, isTenantUpdate) {
|
|
1076
|
+
if (isApplicationUpdate) {
|
|
1077
|
+
return fromToken;
|
|
1078
|
+
}
|
|
1079
|
+
if (isTenantUpdate) {
|
|
1080
|
+
return null;
|
|
1081
|
+
}
|
|
1082
|
+
return fromToken;
|
|
1083
|
+
}
|
|
1084
|
+
isAuthenticatedStatus(status) {
|
|
1085
|
+
return status === AXPSessionStatus.Authenticated || status === AXPSessionStatus.Authorized;
|
|
1086
|
+
}
|
|
1087
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPSessionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1088
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPSessionService, providedIn: 'root' }); }
|
|
730
1089
|
}
|
|
731
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1090
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPSessionService, decorators: [{
|
|
732
1091
|
type: Injectable,
|
|
733
1092
|
args: [{
|
|
734
1093
|
providedIn: 'root',
|
|
@@ -739,7 +1098,8 @@ class AXPPermissionDirective {
|
|
|
739
1098
|
constructor(templateRef, viewContainer) {
|
|
740
1099
|
this.templateRef = templateRef;
|
|
741
1100
|
this.viewContainer = viewContainer;
|
|
742
|
-
this.hasView = signal(false,
|
|
1101
|
+
this.hasView = signal(false, /* @ts-ignore */
|
|
1102
|
+
...(ngDevMode ? [{ debugName: "hasView" }] : /* istanbul ignore next */ []));
|
|
743
1103
|
this.sessionService = inject(AXPSessionService);
|
|
744
1104
|
}
|
|
745
1105
|
set permission(permissionKeys) {
|
|
@@ -774,10 +1134,10 @@ class AXPPermissionDirective {
|
|
|
774
1134
|
ngOnDestroy() {
|
|
775
1135
|
this.subscription?.unsubscribe();
|
|
776
1136
|
}
|
|
777
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
778
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "
|
|
1137
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPPermissionDirective, deps: [{ token: i0.TemplateRef }, { token: i0.ViewContainerRef }], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1138
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.8", type: AXPPermissionDirective, isStandalone: false, selector: "[permission]", inputs: { permission: "permission", permissionElse: "permissionElse" }, ngImport: i0 }); }
|
|
779
1139
|
}
|
|
780
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1140
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPPermissionDirective, decorators: [{
|
|
781
1141
|
type: Directive,
|
|
782
1142
|
args: [{
|
|
783
1143
|
selector: '[permission]',
|
|
@@ -804,26 +1164,37 @@ const AXPPermissionGuard = (route, state) => {
|
|
|
804
1164
|
}));
|
|
805
1165
|
};
|
|
806
1166
|
|
|
1167
|
+
//#endregion
|
|
1168
|
+
//#region ----- Context -----
|
|
807
1169
|
class AXPPermissionDefinitionProviderContext {
|
|
808
1170
|
constructor() {
|
|
809
1171
|
this.builders = new Map();
|
|
810
1172
|
}
|
|
811
|
-
addGroup(name, title, description) {
|
|
1173
|
+
addGroup(name, title, description, options) {
|
|
812
1174
|
if (this.isGroupDefined(name)) {
|
|
813
|
-
|
|
1175
|
+
const existing = this.findGroup(name);
|
|
1176
|
+
if (options?.parent && !existing.group.parent) {
|
|
1177
|
+
existing.group.parent = options.parent;
|
|
1178
|
+
}
|
|
1179
|
+
if (options?.order != null && existing.group.order == null) {
|
|
1180
|
+
existing.group.order = options.order;
|
|
1181
|
+
}
|
|
1182
|
+
return existing;
|
|
814
1183
|
}
|
|
815
1184
|
const group = {
|
|
816
1185
|
name,
|
|
817
1186
|
title,
|
|
818
1187
|
description,
|
|
819
|
-
|
|
1188
|
+
parent: options?.parent,
|
|
1189
|
+
order: options?.order,
|
|
1190
|
+
permissions: [],
|
|
820
1191
|
};
|
|
821
1192
|
const builder = new AXPPermissionDefinitionGroupBuilder(this, group);
|
|
822
1193
|
this.builders.set(name, builder);
|
|
823
1194
|
return builder;
|
|
824
1195
|
}
|
|
825
1196
|
getGroupDefinitions() {
|
|
826
|
-
return Array.from(this.builders.values()).map(b => b.group);
|
|
1197
|
+
return Array.from(this.builders.values()).map((b) => b.group);
|
|
827
1198
|
}
|
|
828
1199
|
findGroup(name) {
|
|
829
1200
|
return this.builders.get(name);
|
|
@@ -832,6 +1203,8 @@ class AXPPermissionDefinitionProviderContext {
|
|
|
832
1203
|
return this.builders.has(name);
|
|
833
1204
|
}
|
|
834
1205
|
}
|
|
1206
|
+
//#endregion
|
|
1207
|
+
//#region ----- Group Builder -----
|
|
835
1208
|
class AXPPermissionDefinitionGroupBuilder {
|
|
836
1209
|
get group() {
|
|
837
1210
|
return this._group;
|
|
@@ -840,13 +1213,20 @@ class AXPPermissionDefinitionGroupBuilder {
|
|
|
840
1213
|
this.context = context;
|
|
841
1214
|
this._group = group;
|
|
842
1215
|
}
|
|
843
|
-
addPermission(name, title, description,
|
|
1216
|
+
addPermission(name, title, description, options) {
|
|
1217
|
+
const normalized = normalizePermissionAddOptions(options);
|
|
1218
|
+
const existing = this._group.permissions.find((p) => p.name === name);
|
|
1219
|
+
if (existing) {
|
|
1220
|
+
mergePermissionOptions(existing, normalized);
|
|
1221
|
+
return new AXPPermissionDefinitionBuilder(this, existing);
|
|
1222
|
+
}
|
|
844
1223
|
const permission = {
|
|
845
1224
|
name,
|
|
846
1225
|
title,
|
|
847
1226
|
description,
|
|
848
1227
|
children: [],
|
|
849
|
-
requiredFeatures
|
|
1228
|
+
requiredFeatures: normalized.requiredFeatures,
|
|
1229
|
+
modules: normalized.modules,
|
|
850
1230
|
};
|
|
851
1231
|
this._group.permissions.push(permission);
|
|
852
1232
|
return new AXPPermissionDefinitionBuilder(this, permission);
|
|
@@ -854,25 +1234,34 @@ class AXPPermissionDefinitionGroupBuilder {
|
|
|
854
1234
|
endGroup() {
|
|
855
1235
|
return this.context;
|
|
856
1236
|
}
|
|
857
|
-
findPermission(
|
|
1237
|
+
findPermission(_path) {
|
|
858
1238
|
return undefined;
|
|
859
1239
|
}
|
|
860
|
-
findGroup(
|
|
1240
|
+
findGroup(_name) {
|
|
861
1241
|
return undefined;
|
|
862
1242
|
}
|
|
863
1243
|
}
|
|
1244
|
+
//#endregion
|
|
1245
|
+
//#region ----- Permission Builder -----
|
|
864
1246
|
class AXPPermissionDefinitionBuilder {
|
|
865
1247
|
constructor(groupBuilder, permission) {
|
|
866
1248
|
this.groupBuilder = groupBuilder;
|
|
867
1249
|
this.permission = permission;
|
|
868
1250
|
}
|
|
869
|
-
addChild(name, title, description,
|
|
1251
|
+
addChild(name, title, description, options) {
|
|
1252
|
+
const normalized = normalizePermissionAddOptions(options);
|
|
1253
|
+
const existing = this.permission.children.find((p) => p.name === name);
|
|
1254
|
+
if (existing) {
|
|
1255
|
+
mergePermissionOptions(existing, normalized);
|
|
1256
|
+
return this;
|
|
1257
|
+
}
|
|
870
1258
|
const permission = {
|
|
871
1259
|
name,
|
|
872
1260
|
title,
|
|
873
1261
|
description,
|
|
874
1262
|
children: [],
|
|
875
|
-
requiredFeatures
|
|
1263
|
+
requiredFeatures: normalized.requiredFeatures,
|
|
1264
|
+
modules: normalized.modules,
|
|
876
1265
|
};
|
|
877
1266
|
this.permission.children.push(permission);
|
|
878
1267
|
return this;
|
|
@@ -885,83 +1274,142 @@ class AXPPermissionDefinitionBuilder {
|
|
|
885
1274
|
this.permission.requiredFeatures = features;
|
|
886
1275
|
return this;
|
|
887
1276
|
}
|
|
1277
|
+
/**
|
|
1278
|
+
* Set edition module ownership for this permission (ANY-match for availability).
|
|
1279
|
+
*/
|
|
1280
|
+
modules(...moduleNames) {
|
|
1281
|
+
this.permission.modules = moduleNames;
|
|
1282
|
+
return this;
|
|
1283
|
+
}
|
|
888
1284
|
endPermission() {
|
|
889
1285
|
return this.groupBuilder;
|
|
890
1286
|
}
|
|
891
1287
|
}
|
|
1288
|
+
//#endregion
|
|
1289
|
+
//#region ----- Helpers -----
|
|
1290
|
+
function normalizePermissionAddOptions(options) {
|
|
1291
|
+
if (!options) {
|
|
1292
|
+
return {};
|
|
1293
|
+
}
|
|
1294
|
+
// Legacy: 4th argument was `requiredFeatures: string[]`
|
|
1295
|
+
if (Array.isArray(options)) {
|
|
1296
|
+
return { requiredFeatures: options };
|
|
1297
|
+
}
|
|
1298
|
+
return options;
|
|
1299
|
+
}
|
|
1300
|
+
function mergePermissionOptions(target, options) {
|
|
1301
|
+
if (options.requiredFeatures?.length) {
|
|
1302
|
+
const set = new Set([...(target.requiredFeatures ?? []), ...options.requiredFeatures]);
|
|
1303
|
+
target.requiredFeatures = Array.from(set);
|
|
1304
|
+
}
|
|
1305
|
+
if (options.modules?.length) {
|
|
1306
|
+
const set = new Set([...(target.modules ?? []), ...options.modules]);
|
|
1307
|
+
target.modules = Array.from(set);
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
//#endregion
|
|
892
1311
|
|
|
893
1312
|
const AXP_PERMISSION_DEFINITION_PROVIDER = new InjectionToken('AXP_PERMISSION_DEFINITION_PROVIDER', {
|
|
894
1313
|
providedIn: 'root',
|
|
895
1314
|
factory: () => {
|
|
896
1315
|
return [];
|
|
897
|
-
}
|
|
1316
|
+
},
|
|
898
1317
|
});
|
|
1318
|
+
const AXP_PERMISSION_MODULES_RESOLVER = new InjectionToken('AXP_PERMISSION_MODULES_RESOLVER');
|
|
899
1319
|
class AXPPermissionDefinitionService {
|
|
900
1320
|
constructor() {
|
|
901
1321
|
this.providers = inject(AXP_PERMISSION_DEFINITION_PROVIDER, { optional: true });
|
|
1322
|
+
this.modulesResolver = inject(AXP_PERMISSION_MODULES_RESOLVER, { optional: true });
|
|
902
1323
|
this.sessionService = inject(AXPSessionService);
|
|
903
1324
|
this.cache = null;
|
|
1325
|
+
/** Invalidates cache when the session edition modules/features change (e.g. app switch). */
|
|
1326
|
+
this.cacheAvailabilityKey = null;
|
|
1327
|
+
}
|
|
1328
|
+
buildAvailabilityKey() {
|
|
1329
|
+
const enabled = this.sessionService.features
|
|
1330
|
+
.filter((feature) => feature.value === true && !!feature.name)
|
|
1331
|
+
.map((feature) => feature.name)
|
|
1332
|
+
.sort();
|
|
1333
|
+
const applicationId = this.sessionService.application?.id ?? '';
|
|
1334
|
+
const tenantId = this.sessionService.tenant?.id ?? '';
|
|
1335
|
+
return `${tenantId}|${applicationId}|${enabled.join(',')}`;
|
|
904
1336
|
}
|
|
905
1337
|
async load() {
|
|
906
|
-
|
|
1338
|
+
const availabilityKey = this.buildAvailabilityKey();
|
|
1339
|
+
if (this.cache && this.cacheAvailabilityKey === availabilityKey) {
|
|
907
1340
|
return;
|
|
908
1341
|
}
|
|
1342
|
+
this.cacheAvailabilityKey = availabilityKey;
|
|
909
1343
|
const context = new AXPPermissionDefinitionProviderContext();
|
|
910
|
-
// Load providers from DI tokens
|
|
911
1344
|
if (Array.isArray(this.providers)) {
|
|
912
1345
|
for (const provider of this.providers) {
|
|
913
1346
|
if (provider instanceof Promise) {
|
|
914
|
-
// If provider is a promise, resolve it
|
|
915
1347
|
const resolvedProvider = await provider;
|
|
916
1348
|
await resolvedProvider.define(context);
|
|
917
1349
|
}
|
|
918
1350
|
else {
|
|
919
|
-
// If provider is a direct instance, use it directly
|
|
920
1351
|
await provider.define(context);
|
|
921
1352
|
}
|
|
922
1353
|
}
|
|
923
1354
|
}
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
this.cache = this.filterByFeatures(allGroups);
|
|
1355
|
+
const allGroups = context.getGroupDefinitions().map((group) => this.enrichGroupModules(group));
|
|
1356
|
+
this.cache = this.filterUnavailablePermissions(allGroups);
|
|
927
1357
|
}
|
|
928
|
-
/**
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
*/
|
|
932
|
-
filterByFeatures(groups) {
|
|
933
|
-
return groups.map(group => ({
|
|
1358
|
+
/** Attach modules[] from the product resolver when providers omitted them. */
|
|
1359
|
+
enrichGroupModules(group) {
|
|
1360
|
+
return {
|
|
934
1361
|
...group,
|
|
935
|
-
permissions:
|
|
936
|
-
}
|
|
1362
|
+
permissions: group.permissions.map((permission) => this.enrichPermissionModules(permission)),
|
|
1363
|
+
};
|
|
1364
|
+
}
|
|
1365
|
+
enrichPermissionModules(permission) {
|
|
1366
|
+
const resolved = permission.modules?.length || !this.modulesResolver
|
|
1367
|
+
? permission.modules
|
|
1368
|
+
: [...this.modulesResolver(permission.name)];
|
|
1369
|
+
return {
|
|
1370
|
+
...permission,
|
|
1371
|
+
modules: resolved,
|
|
1372
|
+
children: (permission.children ?? []).map((child) => this.enrichPermissionModules(child)),
|
|
1373
|
+
};
|
|
937
1374
|
}
|
|
938
1375
|
/**
|
|
939
|
-
*
|
|
1376
|
+
* Filter permissions by edition modules (ANY-match) and required features.
|
|
1377
|
+
* Keeps parent groups that still have at least one child section with permissions.
|
|
940
1378
|
*/
|
|
1379
|
+
filterUnavailablePermissions(groups) {
|
|
1380
|
+
const filtered = groups.map((group) => ({
|
|
1381
|
+
...group,
|
|
1382
|
+
permissions: this.filterPermissions(group.permissions),
|
|
1383
|
+
}));
|
|
1384
|
+
const withPermissions = new Set(filtered.filter((g) => g.permissions.length > 0).map((g) => g.name));
|
|
1385
|
+
const parentsOfKept = new Set(filtered.filter((g) => withPermissions.has(g.name) && g.parent).map((g) => g.parent));
|
|
1386
|
+
return filtered.filter((g) => withPermissions.has(g.name) || parentsOfKept.has(g.name));
|
|
1387
|
+
}
|
|
941
1388
|
filterPermissions(permissions) {
|
|
942
1389
|
return permissions
|
|
943
|
-
.filter(permission => {
|
|
944
|
-
|
|
1390
|
+
.filter((permission) => {
|
|
1391
|
+
if (permission.modules?.length) {
|
|
1392
|
+
const anyModuleEnabled = permission.modules.some((moduleName) => this.sessionService.isModuleEnabled(moduleName));
|
|
1393
|
+
if (!anyModuleEnabled) {
|
|
1394
|
+
return false;
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
945
1397
|
if (permission.requiredFeatures && permission.requiredFeatures.length > 0) {
|
|
946
|
-
const allFeaturesEnabled = permission.requiredFeatures.every(featureName => this.sessionService.isFeatureEnabled(featureName));
|
|
1398
|
+
const allFeaturesEnabled = permission.requiredFeatures.every((featureName) => this.sessionService.isFeatureEnabled(featureName));
|
|
947
1399
|
if (!allFeaturesEnabled) {
|
|
948
|
-
return false;
|
|
1400
|
+
return false;
|
|
949
1401
|
}
|
|
950
1402
|
}
|
|
951
1403
|
return true;
|
|
952
1404
|
})
|
|
953
|
-
.map(permission => ({
|
|
1405
|
+
.map((permission) => ({
|
|
954
1406
|
...permission,
|
|
955
|
-
children: this.filterPermissions(permission.children)
|
|
956
|
-
}))
|
|
957
|
-
.filter(permission => {
|
|
958
|
-
// Remove permissions that have no children if they were only containers
|
|
959
|
-
// (This is optional - you might want to keep parent permissions even without children)
|
|
960
|
-
return true;
|
|
961
|
-
});
|
|
1407
|
+
children: this.filterPermissions(permission.children),
|
|
1408
|
+
}));
|
|
962
1409
|
}
|
|
963
1410
|
async reload() {
|
|
964
1411
|
this.cache = null;
|
|
1412
|
+
this.cacheAvailabilityKey = null;
|
|
965
1413
|
await this.load();
|
|
966
1414
|
}
|
|
967
1415
|
async getGroups() {
|
|
@@ -970,112 +1418,42 @@ class AXPPermissionDefinitionService {
|
|
|
970
1418
|
}
|
|
971
1419
|
async getPermissions() {
|
|
972
1420
|
await this.load();
|
|
973
|
-
return this.cache?.flatMap(g => g.permissions) ?? [];
|
|
1421
|
+
return this.cache?.flatMap((g) => g.permissions) ?? [];
|
|
974
1422
|
}
|
|
975
1423
|
async getGroup(name) {
|
|
976
1424
|
await this.load();
|
|
977
|
-
return this.cache?.find(g => g.name === name) ?? null;
|
|
1425
|
+
return this.cache?.find((g) => g.name === name) ?? null;
|
|
978
1426
|
}
|
|
979
1427
|
async getPermission(name) {
|
|
980
1428
|
await this.load();
|
|
981
|
-
|
|
1429
|
+
for (const group of this.cache ?? []) {
|
|
1430
|
+
const found = this.findPermissionRecursive(group.permissions, name);
|
|
1431
|
+
if (found) {
|
|
1432
|
+
return found;
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
return null;
|
|
982
1436
|
}
|
|
983
|
-
|
|
984
|
-
|
|
1437
|
+
findPermissionRecursive(permissions, name) {
|
|
1438
|
+
for (const permission of permissions) {
|
|
1439
|
+
if (permission.name === name) {
|
|
1440
|
+
return permission;
|
|
1441
|
+
}
|
|
1442
|
+
const child = this.findPermissionRecursive(permission.children ?? [], name);
|
|
1443
|
+
if (child) {
|
|
1444
|
+
return child;
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
return null;
|
|
1448
|
+
}
|
|
1449
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPPermissionDefinitionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1450
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPPermissionDefinitionService, providedIn: 'root' }); }
|
|
985
1451
|
}
|
|
986
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1452
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPPermissionDefinitionService, decorators: [{
|
|
987
1453
|
type: Injectable,
|
|
988
1454
|
args: [{ providedIn: 'root' }]
|
|
989
1455
|
}] });
|
|
990
1456
|
|
|
991
|
-
//#region ---- Imports ----
|
|
992
|
-
//#endregion
|
|
993
|
-
//#region ---- Helpers ----
|
|
994
|
-
function flattenPermissionDefinitions(nodes, groupPrefix) {
|
|
995
|
-
const rows = [];
|
|
996
|
-
for (const node of nodes) {
|
|
997
|
-
const displayTitle = groupPrefix ? `${groupPrefix} / ${node.title}` : node.title;
|
|
998
|
-
if (node.children?.length) {
|
|
999
|
-
rows.push(...flattenPermissionDefinitions(node.children, displayTitle));
|
|
1000
|
-
}
|
|
1001
|
-
else {
|
|
1002
|
-
rows.push({
|
|
1003
|
-
id: String(node.name),
|
|
1004
|
-
title: displayTitle,
|
|
1005
|
-
});
|
|
1006
|
-
}
|
|
1007
|
-
}
|
|
1008
|
-
return rows;
|
|
1009
|
-
}
|
|
1010
|
-
//#endregion
|
|
1011
|
-
//#region ---- Permission definitions data source ----
|
|
1012
|
-
/**
|
|
1013
|
-
* Data source definition for leaf permissions from {@link AXPPermissionDefinitionService#getGroups}.
|
|
1014
|
-
*/
|
|
1015
|
-
class AXPPermissionDefinitionsDataSourceDefinition {
|
|
1016
|
-
constructor() {
|
|
1017
|
-
//#region ---- Services & Dependencies ----
|
|
1018
|
-
this.permissionDefinitionService = inject(AXPPermissionDefinitionService);
|
|
1019
|
-
//#endregion
|
|
1020
|
-
}
|
|
1021
|
-
//#endregion
|
|
1022
|
-
//#region ---- Public API ----
|
|
1023
|
-
async items() {
|
|
1024
|
-
return [
|
|
1025
|
-
{
|
|
1026
|
-
name: AXPPlatformAuthDataSourceKeys.PermissionDefinitions,
|
|
1027
|
-
title: 'Permissions',
|
|
1028
|
-
source: () => new AXDataSource({
|
|
1029
|
-
key: 'id',
|
|
1030
|
-
load: async () => {
|
|
1031
|
-
const groups = await this.permissionDefinitionService.getGroups();
|
|
1032
|
-
const list = [];
|
|
1033
|
-
for (const g of groups) {
|
|
1034
|
-
list.push(...flattenPermissionDefinitions(g.permissions, g.title));
|
|
1035
|
-
}
|
|
1036
|
-
return { items: list, total: list.length };
|
|
1037
|
-
},
|
|
1038
|
-
byKey: async (key) => {
|
|
1039
|
-
const groups = await this.permissionDefinitionService.getGroups();
|
|
1040
|
-
const list = [];
|
|
1041
|
-
for (const g of groups) {
|
|
1042
|
-
list.push(...flattenPermissionDefinitions(g.permissions, g.title));
|
|
1043
|
-
}
|
|
1044
|
-
return list.find((item) => item.id === key);
|
|
1045
|
-
},
|
|
1046
|
-
pageSize: 1000,
|
|
1047
|
-
}),
|
|
1048
|
-
columns: [
|
|
1049
|
-
{
|
|
1050
|
-
name: 'id',
|
|
1051
|
-
title: 'ID',
|
|
1052
|
-
datatype: 'string',
|
|
1053
|
-
type: AXPWidgetsCatalog.text,
|
|
1054
|
-
},
|
|
1055
|
-
{
|
|
1056
|
-
name: 'title',
|
|
1057
|
-
title: 'Title',
|
|
1058
|
-
datatype: 'string',
|
|
1059
|
-
type: AXPWidgetsCatalog.text,
|
|
1060
|
-
},
|
|
1061
|
-
],
|
|
1062
|
-
filters: [
|
|
1063
|
-
{
|
|
1064
|
-
field: 'title',
|
|
1065
|
-
title: 'Title',
|
|
1066
|
-
operator: { type: 'equal' },
|
|
1067
|
-
widget: { type: AXPWidgetsCatalog.text },
|
|
1068
|
-
filterType: { advance: true, inline: true },
|
|
1069
|
-
},
|
|
1070
|
-
],
|
|
1071
|
-
textField: { name: 'title', title: 'Title' },
|
|
1072
|
-
valueField: { name: 'id', title: 'ID' },
|
|
1073
|
-
},
|
|
1074
|
-
];
|
|
1075
|
-
}
|
|
1076
|
-
}
|
|
1077
|
-
//#endregion
|
|
1078
|
-
|
|
1079
1457
|
class AXPPermissionEvaluatorScopeProvider {
|
|
1080
1458
|
constructor() {
|
|
1081
1459
|
this.sessionService = inject(AXPSessionService);
|
|
@@ -1114,16 +1492,6 @@ const AXPAuthGuard = (route, state) => {
|
|
|
1114
1492
|
}));
|
|
1115
1493
|
};
|
|
1116
1494
|
|
|
1117
|
-
function initializeAppState(service) {
|
|
1118
|
-
return async () => {
|
|
1119
|
-
try {
|
|
1120
|
-
await service.restoreSession();
|
|
1121
|
-
}
|
|
1122
|
-
catch (error) {
|
|
1123
|
-
console.error(error);
|
|
1124
|
-
}
|
|
1125
|
-
};
|
|
1126
|
-
}
|
|
1127
1495
|
class AXPAuthModule {
|
|
1128
1496
|
static forRoot(configs) {
|
|
1129
1497
|
return {
|
|
@@ -1165,34 +1533,76 @@ class AXPAuthModule {
|
|
|
1165
1533
|
f();
|
|
1166
1534
|
});
|
|
1167
1535
|
}
|
|
1168
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
1169
|
-
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "
|
|
1170
|
-
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "
|
|
1171
|
-
|
|
1172
|
-
const
|
|
1173
|
-
return
|
|
1536
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPAuthModule, deps: [{ token: 'AXPAuthModuleFactory', optional: true }], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
1537
|
+
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "22.0.8", ngImport: i0, type: AXPAuthModule, declarations: [AXPPermissionDirective, AXPFeatureDirective], exports: [AXPPermissionDirective, AXPFeatureDirective] }); }
|
|
1538
|
+
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPAuthModule, providers: [
|
|
1539
|
+
provideAppStartUpTask(() => {
|
|
1540
|
+
const service = inject(AXPSessionService);
|
|
1541
|
+
return {
|
|
1542
|
+
name: 'session-restore',
|
|
1543
|
+
priority: AXP_APP_STARTUP_PRIORITY.sessionRestore,
|
|
1544
|
+
run: async () => {
|
|
1545
|
+
try {
|
|
1546
|
+
await service.restoreSession();
|
|
1547
|
+
}
|
|
1548
|
+
catch (error) {
|
|
1549
|
+
console.error(error);
|
|
1550
|
+
}
|
|
1551
|
+
},
|
|
1552
|
+
};
|
|
1174
1553
|
}),
|
|
1175
1554
|
{
|
|
1176
1555
|
provide: AXP_SESSION_SERVICE,
|
|
1177
1556
|
useExisting: AXPSessionService,
|
|
1178
1557
|
},
|
|
1558
|
+
{
|
|
1559
|
+
provide: AXP_EXPRESSION_EVALUATOR_SCOPE_PROVIDER,
|
|
1560
|
+
useClass: AXPFeatureEvaluatorScopeProvider,
|
|
1561
|
+
multi: true,
|
|
1562
|
+
},
|
|
1563
|
+
{
|
|
1564
|
+
provide: AXP_EXPRESSION_EVALUATOR_SCOPE_PROVIDER,
|
|
1565
|
+
useClass: AXPPermissionEvaluatorScopeProvider,
|
|
1566
|
+
multi: true,
|
|
1567
|
+
},
|
|
1179
1568
|
] }); }
|
|
1180
1569
|
}
|
|
1181
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1570
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPAuthModule, decorators: [{
|
|
1182
1571
|
type: NgModule,
|
|
1183
1572
|
args: [{
|
|
1184
1573
|
imports: [],
|
|
1185
1574
|
exports: [AXPPermissionDirective, AXPFeatureDirective],
|
|
1186
1575
|
declarations: [AXPPermissionDirective, AXPFeatureDirective],
|
|
1187
1576
|
providers: [
|
|
1188
|
-
|
|
1189
|
-
const
|
|
1190
|
-
return
|
|
1577
|
+
provideAppStartUpTask(() => {
|
|
1578
|
+
const service = inject(AXPSessionService);
|
|
1579
|
+
return {
|
|
1580
|
+
name: 'session-restore',
|
|
1581
|
+
priority: AXP_APP_STARTUP_PRIORITY.sessionRestore,
|
|
1582
|
+
run: async () => {
|
|
1583
|
+
try {
|
|
1584
|
+
await service.restoreSession();
|
|
1585
|
+
}
|
|
1586
|
+
catch (error) {
|
|
1587
|
+
console.error(error);
|
|
1588
|
+
}
|
|
1589
|
+
},
|
|
1590
|
+
};
|
|
1191
1591
|
}),
|
|
1192
1592
|
{
|
|
1193
1593
|
provide: AXP_SESSION_SERVICE,
|
|
1194
1594
|
useExisting: AXPSessionService,
|
|
1195
1595
|
},
|
|
1596
|
+
{
|
|
1597
|
+
provide: AXP_EXPRESSION_EVALUATOR_SCOPE_PROVIDER,
|
|
1598
|
+
useClass: AXPFeatureEvaluatorScopeProvider,
|
|
1599
|
+
multi: true,
|
|
1600
|
+
},
|
|
1601
|
+
{
|
|
1602
|
+
provide: AXP_EXPRESSION_EVALUATOR_SCOPE_PROVIDER,
|
|
1603
|
+
useClass: AXPPermissionEvaluatorScopeProvider,
|
|
1604
|
+
multi: true,
|
|
1605
|
+
},
|
|
1196
1606
|
],
|
|
1197
1607
|
}]
|
|
1198
1608
|
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
@@ -1204,6 +1614,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
1204
1614
|
|
|
1205
1615
|
class AXPAuthStrategy {
|
|
1206
1616
|
}
|
|
1617
|
+
//#endregion
|
|
1207
1618
|
|
|
1208
1619
|
//#region ---- JWT Utility ----
|
|
1209
1620
|
/**
|
|
@@ -1292,40 +1703,13 @@ function platformOAuthSessionUpdateParams(params) {
|
|
|
1292
1703
|
applicationId: params['applicationId'] ?? application?.id,
|
|
1293
1704
|
};
|
|
1294
1705
|
}
|
|
1295
|
-
function shouldPlatformOAuthBindSession(
|
|
1296
|
-
if (!
|
|
1706
|
+
function shouldPlatformOAuthBindSession(hasCredentials, params) {
|
|
1707
|
+
if (!hasCredentials) {
|
|
1297
1708
|
return false;
|
|
1298
1709
|
}
|
|
1299
1710
|
const { tenant, application, tenantId, applicationId } = platformOAuthSessionUpdateParams(params);
|
|
1300
1711
|
return Boolean(tenantId || applicationId || tenant || application);
|
|
1301
1712
|
}
|
|
1302
|
-
async function postPlatformOAuthSessionUpdate(baseUrl, accessToken, params) {
|
|
1303
|
-
const { tenant, application, tenantId, applicationId } = platformOAuthSessionUpdateParams(params);
|
|
1304
|
-
const response = await fetch(`${baseUrl.replace(/\/$/, '')}/v1/auth/session`, {
|
|
1305
|
-
method: 'POST',
|
|
1306
|
-
headers: {
|
|
1307
|
-
'Content-Type': 'application/json',
|
|
1308
|
-
Authorization: `Bearer ${accessToken}`,
|
|
1309
|
-
},
|
|
1310
|
-
credentials: 'include',
|
|
1311
|
-
body: JSON.stringify({
|
|
1312
|
-
tenantId,
|
|
1313
|
-
applicationId,
|
|
1314
|
-
tenant,
|
|
1315
|
-
application,
|
|
1316
|
-
}),
|
|
1317
|
-
});
|
|
1318
|
-
if (!response.ok) {
|
|
1319
|
-
const detail = await response.text().catch(() => '');
|
|
1320
|
-
throw new Error(`Session update failed: ${response.status} ${response.statusText}${detail ? ` — ${detail}` : ''}`);
|
|
1321
|
-
}
|
|
1322
|
-
const result = (await response.json());
|
|
1323
|
-
return toPlatformOAuthSignInResult(result, {
|
|
1324
|
-
user: params['user'] ?? null,
|
|
1325
|
-
tenant: tenant ?? null,
|
|
1326
|
-
application: application ?? null,
|
|
1327
|
-
});
|
|
1328
|
-
}
|
|
1329
1713
|
function toPlatformOAuthSignInResult(result, context) {
|
|
1330
1714
|
const tenant = mergeTenant(mapPlatformTenant(result.tenant), context?.tenant ?? null);
|
|
1331
1715
|
const application = mergeApplication(mapPlatformApplication(result.application), context?.application ?? null);
|
|
@@ -1408,6 +1792,137 @@ function parsePlatformExpiresIn(expiresIn) {
|
|
|
1408
1792
|
}
|
|
1409
1793
|
//#endregion
|
|
1410
1794
|
|
|
1795
|
+
//#region ---- Platform OAuth helpers (PKCE + /connect/*) ----
|
|
1796
|
+
const AXP_DEFAULT_PLATFORM_OAUTH_CLIENT_ID = 'acorex';
|
|
1797
|
+
const AXP_DEFAULT_PLATFORM_OAUTH_SCOPE = 'openid profile email offline_access';
|
|
1798
|
+
function resolvePlatformOAuthIssuer(baseUrl) {
|
|
1799
|
+
const issuer = baseUrl?.replace(/\/$/, '');
|
|
1800
|
+
if (!issuer) {
|
|
1801
|
+
throw new Error('API baseUrl is not configured');
|
|
1802
|
+
}
|
|
1803
|
+
return issuer;
|
|
1804
|
+
}
|
|
1805
|
+
function resolvePlatformOAuthRedirectUri() {
|
|
1806
|
+
return `${window.location.origin}/auth/oauth-callback`;
|
|
1807
|
+
}
|
|
1808
|
+
function resolvePlatformOAuthClientConfig(authConfig) {
|
|
1809
|
+
return {
|
|
1810
|
+
clientId: authConfig?.clientId ?? authConfig?.client_id ?? AXP_DEFAULT_PLATFORM_OAUTH_CLIENT_ID,
|
|
1811
|
+
redirectUri: authConfig?.redirectUri ?? resolvePlatformOAuthRedirectUri(),
|
|
1812
|
+
scope: authConfig?.scope ?? AXP_DEFAULT_PLATFORM_OAUTH_SCOPE,
|
|
1813
|
+
};
|
|
1814
|
+
}
|
|
1815
|
+
async function fetchPlatformOAuthDiscoveryDocument(issuer) {
|
|
1816
|
+
const response = await fetch(`${issuer}/.well-known/openid-configuration`);
|
|
1817
|
+
if (!response.ok) {
|
|
1818
|
+
throw new Error(`Failed to load OpenID discovery document: ${response.status} ${response.statusText}`);
|
|
1819
|
+
}
|
|
1820
|
+
return (await response.json());
|
|
1821
|
+
}
|
|
1822
|
+
function resolvePlatformOAuthAuthorizeEndpoint(issuer, discovery) {
|
|
1823
|
+
return discovery?.authorization_endpoint ?? `${issuer}/connect/authorize`;
|
|
1824
|
+
}
|
|
1825
|
+
function resolvePlatformOAuthTokenEndpoint(issuer, discovery) {
|
|
1826
|
+
return discovery?.token_endpoint ?? `${issuer}/connect/token`;
|
|
1827
|
+
}
|
|
1828
|
+
//#endregion
|
|
1829
|
+
|
|
1830
|
+
//#region ---- Auth:ListProviders public DTOs ----
|
|
1831
|
+
function asAuthListProvidersResponse(payload) {
|
|
1832
|
+
const data = (payload ?? {});
|
|
1833
|
+
return {
|
|
1834
|
+
localMethods: Array.isArray(data.localMethods) ? data.localMethods : [],
|
|
1835
|
+
identityProviders: Array.isArray(data.identityProviders) ? data.identityProviders : [],
|
|
1836
|
+
};
|
|
1837
|
+
}
|
|
1838
|
+
function findIdentityProvider(payload, id) {
|
|
1839
|
+
const { identityProviders } = asAuthListProvidersResponse(payload);
|
|
1840
|
+
return identityProviders.find((p) => p.id === id);
|
|
1841
|
+
}
|
|
1842
|
+
//#endregion
|
|
1843
|
+
|
|
1844
|
+
/**
|
|
1845
|
+
* Best-effort plain string for thrown {@link Error} messages from command results (no i18n lookup).
|
|
1846
|
+
*/
|
|
1847
|
+
function commandMessageTextForError(value) {
|
|
1848
|
+
if (value == null) {
|
|
1849
|
+
return '';
|
|
1850
|
+
}
|
|
1851
|
+
if (typeof value === 'string') {
|
|
1852
|
+
return value;
|
|
1853
|
+
}
|
|
1854
|
+
if (typeof value === 'object' && !Array.isArray(value)) {
|
|
1855
|
+
const values = Object.values(value);
|
|
1856
|
+
const first = values.find((v) => typeof v === 'string' && v.length > 0);
|
|
1857
|
+
return first ?? '';
|
|
1858
|
+
}
|
|
1859
|
+
return String(value);
|
|
1860
|
+
}
|
|
1861
|
+
|
|
1862
|
+
class AXPPlatformAuthRuntimeService {
|
|
1863
|
+
constructor() {
|
|
1864
|
+
this.commandService = inject(AXPCommandService);
|
|
1865
|
+
this.queryService = inject(AXPQueryService);
|
|
1866
|
+
this.sessionService = inject(AXPSessionService);
|
|
1867
|
+
this.listProvidersCache = null;
|
|
1868
|
+
this.listProvidersInFlight = null;
|
|
1869
|
+
}
|
|
1870
|
+
async logout(_refreshToken) {
|
|
1871
|
+
try {
|
|
1872
|
+
// Refresh token is read from HttpOnly cookie on the server.
|
|
1873
|
+
await this.commandService.execute(AUTH_LOGOUT_DEFAULT, {});
|
|
1874
|
+
}
|
|
1875
|
+
catch {
|
|
1876
|
+
// Ignore logout network errors; client session is cleared regardless.
|
|
1877
|
+
}
|
|
1878
|
+
finally {
|
|
1879
|
+
this.clearListProvidersCache();
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1882
|
+
async refresh(_refreshToken) {
|
|
1883
|
+
return this.executeCommand(AUTH_REFRESH_DEFAULT, {});
|
|
1884
|
+
}
|
|
1885
|
+
async updateSession(input) {
|
|
1886
|
+
return this.executeCommand(AUTH_UPDATE_SESSION_DEFAULT, input);
|
|
1887
|
+
}
|
|
1888
|
+
async listProviders() {
|
|
1889
|
+
if (this.listProvidersCache) {
|
|
1890
|
+
return this.listProvidersCache;
|
|
1891
|
+
}
|
|
1892
|
+
if (this.listProvidersInFlight) {
|
|
1893
|
+
return this.listProvidersInFlight;
|
|
1894
|
+
}
|
|
1895
|
+
this.listProvidersInFlight = this.queryService
|
|
1896
|
+
.fetch(AUTH_LIST_PROVIDERS_DEFAULT, {})
|
|
1897
|
+
.then((result) => {
|
|
1898
|
+
this.listProvidersCache = asAuthListProvidersResponse(runtimeQueryPayloadData(result));
|
|
1899
|
+
return this.listProvidersCache;
|
|
1900
|
+
})
|
|
1901
|
+
.finally(() => {
|
|
1902
|
+
this.listProvidersInFlight = null;
|
|
1903
|
+
});
|
|
1904
|
+
return this.listProvidersInFlight;
|
|
1905
|
+
}
|
|
1906
|
+
clearListProvidersCache() {
|
|
1907
|
+
this.listProvidersCache = null;
|
|
1908
|
+
this.listProvidersInFlight = null;
|
|
1909
|
+
}
|
|
1910
|
+
async executeCommand(key, input) {
|
|
1911
|
+
const result = await this.commandService.execute(key, input);
|
|
1912
|
+
const data = runtimeCommandPayloadData(result);
|
|
1913
|
+
if (data == null) {
|
|
1914
|
+
throw new Error(commandMessageTextForError(result?.message?.text) || `Auth command failed: ${key}`);
|
|
1915
|
+
}
|
|
1916
|
+
return data;
|
|
1917
|
+
}
|
|
1918
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPPlatformAuthRuntimeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1919
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPPlatformAuthRuntimeService, providedIn: 'root' }); }
|
|
1920
|
+
}
|
|
1921
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPPlatformAuthRuntimeService, decorators: [{
|
|
1922
|
+
type: Injectable,
|
|
1923
|
+
args: [{ providedIn: 'root' }]
|
|
1924
|
+
}] });
|
|
1925
|
+
|
|
1411
1926
|
const AXP_USER_AVATAR_CACHE_PORT = new InjectionToken('AXP_USER_AVATAR_CACHE_PORT');
|
|
1412
1927
|
|
|
1413
1928
|
//#region ---- User profile identity service ----
|
|
@@ -1442,10 +1957,10 @@ class AXPUserProfileIdentityService {
|
|
|
1442
1957
|
}
|
|
1443
1958
|
await this.avatarCachePort?.refreshUserInfo(id);
|
|
1444
1959
|
}
|
|
1445
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
1446
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
1960
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPUserProfileIdentityService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1961
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPUserProfileIdentityService, providedIn: 'root' }); }
|
|
1447
1962
|
}
|
|
1448
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1963
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPUserProfileIdentityService, decorators: [{
|
|
1449
1964
|
type: Injectable,
|
|
1450
1965
|
args: [{ providedIn: 'root' }]
|
|
1451
1966
|
}] });
|
|
@@ -1581,11 +2096,13 @@ class AXPLoginChallengeComponentBase {
|
|
|
1581
2096
|
* Challenge data to display
|
|
1582
2097
|
* Contains the image/content and metadata from the server
|
|
1583
2098
|
*/
|
|
1584
|
-
this.challengeData = input.required(
|
|
2099
|
+
this.challengeData = input.required(/* @ts-ignore */
|
|
2100
|
+
...(ngDevMode ? [{ debugName: "challengeData" }] : /* istanbul ignore next */ []));
|
|
1585
2101
|
/**
|
|
1586
2102
|
* Whether the challenge is currently loading (e.g., refreshing)
|
|
1587
2103
|
*/
|
|
1588
|
-
this.isLoading = input(false,
|
|
2104
|
+
this.isLoading = input(false, /* @ts-ignore */
|
|
2105
|
+
...(ngDevMode ? [{ debugName: "isLoading" }] : /* istanbul ignore next */ []));
|
|
1589
2106
|
//#endregion
|
|
1590
2107
|
//#region ---- Outputs ----
|
|
1591
2108
|
/**
|
|
@@ -1599,20 +2116,61 @@ class AXPLoginChallengeComponentBase {
|
|
|
1599
2116
|
*/
|
|
1600
2117
|
this.refreshRequest = output();
|
|
1601
2118
|
}
|
|
1602
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
1603
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "
|
|
2119
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPLoginChallengeComponentBase, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2120
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.8", 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, changeDetection: i0.ChangeDetectionStrategy.Eager }); }
|
|
1604
2121
|
}
|
|
1605
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
2122
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPLoginChallengeComponentBase, decorators: [{
|
|
1606
2123
|
type: Component,
|
|
1607
2124
|
args: [{
|
|
1608
2125
|
template: '',
|
|
2126
|
+
changeDetection: ChangeDetectionStrategy.Eager,
|
|
1609
2127
|
standalone: true,
|
|
1610
2128
|
}]
|
|
1611
2129
|
}], 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"] }] } });
|
|
1612
2130
|
|
|
2131
|
+
//#region ----- Sign-in method types -----
|
|
2132
|
+
/**
|
|
2133
|
+
* Pluggable sign-in method. Multiple methods can be registered and offered at once.
|
|
2134
|
+
* Session remember/refresh live in {@link AXPSessionService}; methods only prove identity.
|
|
2135
|
+
*/
|
|
2136
|
+
class AXPSignInMethod {
|
|
2137
|
+
}
|
|
2138
|
+
//#endregion
|
|
2139
|
+
|
|
2140
|
+
//#region ----- Sign-in method registry -----
|
|
2141
|
+
/**
|
|
2142
|
+
* Registry of enabled sign-in methods for the login UI.
|
|
2143
|
+
* Methods are independent of session refresh/remember (owned by session service).
|
|
2144
|
+
*/
|
|
2145
|
+
class AXPSignInMethodRegistry {
|
|
2146
|
+
constructor() {
|
|
2147
|
+
this.methods = new Map();
|
|
2148
|
+
}
|
|
2149
|
+
register(...methods) {
|
|
2150
|
+
for (const method of methods) {
|
|
2151
|
+
this.methods.set(method.descriptor.id, method);
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
get(id) {
|
|
2155
|
+
return this.methods.get(id);
|
|
2156
|
+
}
|
|
2157
|
+
listEnabled() {
|
|
2158
|
+
return [...this.methods.values()]
|
|
2159
|
+
.map((m) => m.descriptor)
|
|
2160
|
+
.filter((d) => d.enabled)
|
|
2161
|
+
.sort((a, b) => (a.priority ?? 100) - (b.priority ?? 100));
|
|
2162
|
+
}
|
|
2163
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPSignInMethodRegistry, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
2164
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPSignInMethodRegistry, providedIn: 'root' }); }
|
|
2165
|
+
}
|
|
2166
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPSignInMethodRegistry, decorators: [{
|
|
2167
|
+
type: Injectable,
|
|
2168
|
+
args: [{ providedIn: 'root' }]
|
|
2169
|
+
}] });
|
|
2170
|
+
|
|
1613
2171
|
/**
|
|
1614
2172
|
* Generated bundle index. Do not edit.
|
|
1615
2173
|
*/
|
|
1616
2174
|
|
|
1617
|
-
export { AXPAuthGuard, AXPAuthModule, AXPAuthStrategy, AXPAuthStrategyRegistryService, AXPFeatureDirective, AXPFeatureGuard, AXPLoginChallengeComponentBase, AXPLoginChallengeProvider, AXPPermissionDefinitionBuilder, AXPPermissionDefinitionGroupBuilder, AXPPermissionDefinitionProviderContext, AXPPermissionDefinitionService,
|
|
2175
|
+
export { AUTH_SESSION_TRACE_NS, AUTH_SESSION_TRACE_TITLE, AXPAuthGuard, AXPAuthModule, AXPAuthStrategy, AXPAuthStrategyRegistryService, AXPFeatureDirective, AXPFeatureEvaluatorScopeProvider, AXPFeatureGuard, AXPLoginChallengeComponentBase, AXPLoginChallengeProvider, AXPPermissionDefinitionBuilder, AXPPermissionDefinitionGroupBuilder, AXPPermissionDefinitionProviderContext, AXPPermissionDefinitionService, AXPPermissionDirective, AXPPermissionEvaluatorScopeProvider, AXPPermissionGuard, AXPPlatformAuthRuntimeService, AXPSessionContext, AXPSessionService, AXPSessionStatus, AXPSignInMethod, AXPSignInMethodRegistry, AXPUnauthenticatedError, AXPUnauthorizedError, AXPUserProfileIdentityService, AXP_ACCESS_POLICY_CACHE_PORT, AXP_APPLICATION_LOADER, AXP_DEFAULT_PLATFORM_OAUTH_CLIENT_ID, AXP_DEFAULT_PLATFORM_OAUTH_SCOPE, AXP_FEATURE_CHECKER, AXP_FEATURE_LOADER, AXP_LOGIN_CHALLENGE_PROVIDER, AXP_OAUTH_PENDING_STRATEGY_KEY, AXP_OAUTH_PKCE_VERIFIER_KEY, AXP_PERMISSION_CHECKER, AXP_PERMISSION_DEFINITION_PROVIDER, AXP_PERMISSION_LOADER, AXP_PERMISSION_MODULES_RESOLVER, AXP_TENANT_LOADER, AXP_USER_AVATAR_CACHE_PORT, JwtUtil, PkceUtil, TimeUtil, asAuthListProvidersResponse, clearOAuthPendingStrategy, commandMessageTextForError, fetchPlatformOAuthDiscoveryDocument, findIdentityProvider, getOAuthPendingStrategy, isOAuthRedirectInFlight, platformOAuthSessionUpdateParams, resolvePlatformOAuthAuthorizeEndpoint, resolvePlatformOAuthClientConfig, resolvePlatformOAuthIssuer, resolvePlatformOAuthRedirectUri, resolvePlatformOAuthTokenEndpoint, setOAuthPendingStrategy, shouldPlatformOAuthBindSession, toPlatformOAuthSignInResult, traceAuthAccessPolicy, traceAuthCatalog, traceAuthGuard, traceAuthLifecycle, traceAuthLifecycleError, traceAuthRestore, traceAuthWorkspace };
|
|
1618
2176
|
//# sourceMappingURL=acorex-platform-framework-client-auth.mjs.map
|