@acorex-platform/framework-client 0.0.38 → 0.0.48
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 +212 -44
- package/fesm2022/acorex-platform-framework-client-auth.mjs.map +1 -1
- package/fesm2022/{acorex-platform-framework-client-common-common-settings.provider-CzBr0cRi.mjs → acorex-platform-framework-client-common-common-settings.provider-ChU2tvyt.mjs} +31 -1
- package/fesm2022/acorex-platform-framework-client-common-common-settings.provider-ChU2tvyt.mjs.map +1 -0
- package/fesm2022/acorex-platform-framework-client-common.mjs +1172 -193
- package/fesm2022/acorex-platform-framework-client-common.mjs.map +1 -1
- package/fesm2022/acorex-platform-framework-client-core.mjs +233 -132
- package/fesm2022/acorex-platform-framework-client-core.mjs.map +1 -1
- package/fesm2022/acorex-platform-framework-client-layout-builder.mjs +295 -59
- 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-Bp2cNCPO.mjs → acorex-platform-framework-client-layout-components-binding-expression-editor-popup.component-BqPaHKPM.mjs} +10 -6
- package/fesm2022/acorex-platform-framework-client-layout-components-binding-expression-editor-popup.component-BqPaHKPM.mjs.map +1 -0
- package/fesm2022/acorex-platform-framework-client-layout-components-property-viewer-inline-code-editor.component-CcpUTEVQ.mjs +66 -0
- package/fesm2022/acorex-platform-framework-client-layout-components-property-viewer-inline-code-editor.component-CcpUTEVQ.mjs.map +1 -0
- package/fesm2022/acorex-platform-framework-client-layout-components-resource-appointment-board.component-DCkpgsFH.mjs +195 -0
- package/fesm2022/acorex-platform-framework-client-layout-components-resource-appointment-board.component-DCkpgsFH.mjs.map +1 -0
- package/fesm2022/acorex-platform-framework-client-layout-components.mjs +4015 -1014
- package/fesm2022/acorex-platform-framework-client-layout-components.mjs.map +1 -1
- package/fesm2022/acorex-platform-framework-client-layout-designer.mjs +1179 -304
- 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-CvaOWbJx.mjs → acorex-platform-framework-client-layout-entity-acorex-platform-framework-client-layout-entity-UkQsf84C.mjs} +1701 -300
- package/fesm2022/acorex-platform-framework-client-layout-entity-acorex-platform-framework-client-layout-entity-UkQsf84C.mjs.map +1 -0
- package/fesm2022/{acorex-platform-framework-client-layout-entity-attachments-page.component-Ds63Rt2n.mjs → acorex-platform-framework-client-layout-entity-attachments-page.component-DmmiDLZw.mjs} +2 -2
- package/fesm2022/{acorex-platform-framework-client-layout-entity-attachments-page.component-Ds63Rt2n.mjs.map → acorex-platform-framework-client-layout-entity-attachments-page.component-DmmiDLZw.mjs.map} +1 -1
- package/fesm2022/{acorex-platform-framework-client-layout-entity-bulk-entity.command-C0pohwdo.mjs → acorex-platform-framework-client-layout-entity-bulk-entity.command-BOFTb66c.mjs} +2 -2
- package/fesm2022/{acorex-platform-framework-client-layout-entity-bulk-entity.command-C0pohwdo.mjs.map → acorex-platform-framework-client-layout-entity-bulk-entity.command-BOFTb66c.mjs.map} +1 -1
- package/fesm2022/{acorex-platform-framework-client-layout-entity-gallery-page.component-o2HuZrDN.mjs → acorex-platform-framework-client-layout-entity-gallery-page.component-Bg-cMOlQ.mjs} +2 -2
- package/fesm2022/{acorex-platform-framework-client-layout-entity-gallery-page.component-o2HuZrDN.mjs.map → acorex-platform-framework-client-layout-entity-gallery-page.component-Bg-cMOlQ.mjs.map} +1 -1
- package/fesm2022/acorex-platform-framework-client-layout-entity.mjs +2 -2
- package/fesm2022/acorex-platform-framework-client-layout-views.mjs +47 -8
- package/fesm2022/acorex-platform-framework-client-layout-views.mjs.map +1 -1
- package/fesm2022/acorex-platform-framework-client-layout-widget-core.mjs +3854 -3292
- package/fesm2022/acorex-platform-framework-client-layout-widget-core.mjs.map +1 -1
- package/fesm2022/acorex-platform-framework-client-layout-widgets-acorex-platform-framework-client-layout-widgets-DJNSBNVe.mjs +33319 -0
- package/fesm2022/acorex-platform-framework-client-layout-widgets-acorex-platform-framework-client-layout-widgets-DJNSBNVe.mjs.map +1 -0
- package/fesm2022/{acorex-platform-framework-client-layout-widgets-button-widget-designer.component-BDOdcoyN.mjs → acorex-platform-framework-client-layout-widgets-button-widget-designer.component-BkrPu1wO.mjs} +8 -5
- package/fesm2022/acorex-platform-framework-client-layout-widgets-button-widget-designer.component-BkrPu1wO.mjs.map +1 -0
- package/fesm2022/acorex-platform-framework-client-layout-widgets-code-editor-value.util-Cb7R1qLO.mjs +45 -0
- package/fesm2022/acorex-platform-framework-client-layout-widgets-code-editor-value.util-Cb7R1qLO.mjs.map +1 -0
- package/fesm2022/acorex-platform-framework-client-layout-widgets-code-editor-widget-column.component-BL621E1r.mjs +68 -0
- package/fesm2022/acorex-platform-framework-client-layout-widgets-code-editor-widget-column.component-BL621E1r.mjs.map +1 -0
- package/fesm2022/acorex-platform-framework-client-layout-widgets-code-editor-widget-edit.component-CVsPKhNl.mjs +88 -0
- package/fesm2022/acorex-platform-framework-client-layout-widgets-code-editor-widget-edit.component-CVsPKhNl.mjs.map +1 -0
- package/fesm2022/acorex-platform-framework-client-layout-widgets-code-editor-widget-view.component-BEq4O4al.mjs +71 -0
- package/fesm2022/acorex-platform-framework-client-layout-widgets-code-editor-widget-view.component-BEq4O4al.mjs.map +1 -0
- package/fesm2022/acorex-platform-framework-client-layout-widgets-condition-builder-edit-popup.component-WpiEHusZ.mjs +81 -0
- package/fesm2022/acorex-platform-framework-client-layout-widgets-condition-builder-edit-popup.component-WpiEHusZ.mjs.map +1 -0
- package/fesm2022/{acorex-platform-framework-client-layout-widgets-page-widget-designer.component-WzIiwnGC.mjs → acorex-platform-framework-client-layout-widgets-page-widget-designer.component-Cadl6guA.mjs} +41 -23
- package/fesm2022/acorex-platform-framework-client-layout-widgets-page-widget-designer.component-Cadl6guA.mjs.map +1 -0
- package/fesm2022/{acorex-platform-framework-client-layout-widgets-repeater-widget-column.component-4mX8aYzA.mjs → acorex-platform-framework-client-layout-widgets-repeater-widget-column.component-CKtGsWPI.mjs} +3 -3
- package/fesm2022/{acorex-platform-framework-client-layout-widgets-repeater-widget-column.component-4mX8aYzA.mjs.map → acorex-platform-framework-client-layout-widgets-repeater-widget-column.component-CKtGsWPI.mjs.map} +1 -1
- package/fesm2022/acorex-platform-framework-client-layout-widgets-scheduler-picker-widget-column.component-Clb27Loe.mjs +71 -0
- package/fesm2022/acorex-platform-framework-client-layout-widgets-scheduler-picker-widget-column.component-Clb27Loe.mjs.map +1 -0
- package/fesm2022/acorex-platform-framework-client-layout-widgets-scheduler-picker-widget-edit.component-l7q8A-D3.mjs +111 -0
- package/fesm2022/acorex-platform-framework-client-layout-widgets-scheduler-picker-widget-edit.component-l7q8A-D3.mjs.map +1 -0
- package/fesm2022/acorex-platform-framework-client-layout-widgets-scheduler-picker-widget-view.component-Cc1AH09b.mjs +77 -0
- package/fesm2022/acorex-platform-framework-client-layout-widgets-scheduler-picker-widget-view.component-Cc1AH09b.mjs.map +1 -0
- package/fesm2022/{acorex-platform-framework-client-layout-widgets-tabular-data-edit-popup.component-TWxthl21.mjs → acorex-platform-framework-client-layout-widgets-tabular-data-edit-popup.component-BCLxS3rS.mjs} +3 -3
- package/fesm2022/{acorex-platform-framework-client-layout-widgets-tabular-data-edit-popup.component-TWxthl21.mjs.map → acorex-platform-framework-client-layout-widgets-tabular-data-edit-popup.component-BCLxS3rS.mjs.map} +1 -1
- package/fesm2022/{acorex-platform-framework-client-layout-widgets-text-block-widget-designer.component-D6HFPmGC.mjs → acorex-platform-framework-client-layout-widgets-text-block-widget-designer.component-DYxgwR2E.mjs} +21 -8
- package/fesm2022/acorex-platform-framework-client-layout-widgets-text-block-widget-designer.component-DYxgwR2E.mjs.map +1 -0
- package/fesm2022/acorex-platform-framework-client-layout-widgets-trigger-expression-editor-popup.component-C2PGoC7r.mjs +124 -0
- package/fesm2022/acorex-platform-framework-client-layout-widgets-trigger-expression-editor-popup.component-C2PGoC7r.mjs.map +1 -0
- package/fesm2022/acorex-platform-framework-client-layout-widgets-triggers-widget-editor.component-DDDoPoQa.mjs +345 -0
- package/fesm2022/acorex-platform-framework-client-layout-widgets-triggers-widget-editor.component-DDDoPoQa.mjs.map +1 -0
- package/fesm2022/acorex-platform-framework-client-layout-widgets.mjs +1 -31985
- package/fesm2022/acorex-platform-framework-client-layout-widgets.mjs.map +1 -1
- package/fesm2022/acorex-platform-framework-client-realtime.mjs +2 -1
- package/fesm2022/acorex-platform-framework-client-realtime.mjs.map +1 -1
- package/fesm2022/acorex-platform-framework-client-runtime.mjs +2 -2
- package/fesm2022/acorex-platform-framework-client-runtime.mjs.map +1 -1
- package/fesm2022/{acorex-platform-framework-client-themes-default-entity-master-create-view.component-CT7WWWG0.mjs → acorex-platform-framework-client-themes-default-entity-master-create-view.component-DlS4v_zb.mjs} +3 -3
- package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-create-view.component-DlS4v_zb.mjs.map +1 -0
- package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-modify-view.component-DBmdBKBL.mjs +122 -0
- package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-modify-view.component-DBmdBKBL.mjs.map +1 -0
- package/fesm2022/{acorex-platform-framework-client-themes-default-entity-master-single-view.component-DBUwSzvg.mjs → acorex-platform-framework-client-themes-default-entity-master-single-view.component-CkpRT1a9.mjs} +3 -3
- package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-single-view.component-CkpRT1a9.mjs.map +1 -0
- package/fesm2022/acorex-platform-framework-client-themes-default.mjs +424 -103
- package/fesm2022/acorex-platform-framework-client-themes-default.mjs.map +1 -1
- package/fesm2022/{acorex-platform-framework-client-themes-shared-settings.provider-CpsAIsxj.mjs → acorex-platform-framework-client-themes-shared-settings.provider-DIkjSTU7.mjs} +102 -1
- package/fesm2022/acorex-platform-framework-client-themes-shared-settings.provider-DIkjSTU7.mjs.map +1 -0
- package/fesm2022/acorex-platform-framework-client-themes-shared.mjs +174 -22
- package/fesm2022/acorex-platform-framework-client-themes-shared.mjs.map +1 -1
- package/package.json +2 -2
- package/realtime/README.md +1 -1
- package/types/acorex-platform-framework-client-auth.d.ts +18 -1
- package/types/acorex-platform-framework-client-common.d.ts +243 -9
- package/types/acorex-platform-framework-client-core.d.ts +152 -119
- package/types/acorex-platform-framework-client-layout-builder.d.ts +29 -0
- package/types/acorex-platform-framework-client-layout-components.d.ts +717 -147
- package/types/acorex-platform-framework-client-layout-designer.d.ts +192 -14
- package/types/acorex-platform-framework-client-layout-entity.d.ts +167 -92
- package/types/acorex-platform-framework-client-layout-views.d.ts +23 -1
- package/types/acorex-platform-framework-client-layout-widget-core.d.ts +148 -40
- package/types/acorex-platform-framework-client-layout-widgets.d.ts +261 -119
- package/types/acorex-platform-framework-client-realtime.d.ts +16 -3
- package/types/acorex-platform-framework-client-themes-default.d.ts +62 -11
- package/types/acorex-platform-framework-client-themes-shared.d.ts +25 -2
- package/fesm2022/acorex-platform-framework-client-common-common-settings.provider-CzBr0cRi.mjs.map +0 -1
- package/fesm2022/acorex-platform-framework-client-layout-components-binding-expression-editor-popup.component-Bp2cNCPO.mjs.map +0 -1
- package/fesm2022/acorex-platform-framework-client-layout-components-file-provider-picker.popup.component-BtLEUWAA.mjs +0 -77
- package/fesm2022/acorex-platform-framework-client-layout-components-file-provider-picker.popup.component-BtLEUWAA.mjs.map +0 -1
- package/fesm2022/acorex-platform-framework-client-layout-entity-acorex-platform-framework-client-layout-entity-CvaOWbJx.mjs.map +0 -1
- package/fesm2022/acorex-platform-framework-client-layout-widgets-button-widget-designer.component-BDOdcoyN.mjs.map +0 -1
- package/fesm2022/acorex-platform-framework-client-layout-widgets-page-widget-designer.component-WzIiwnGC.mjs.map +0 -1
- package/fesm2022/acorex-platform-framework-client-layout-widgets-text-block-widget-designer.component-D6HFPmGC.mjs.map +0 -1
- package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-create-view.component-CT7WWWG0.mjs.map +0 -1
- package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-modify-view.component-DienTcPE.mjs +0 -122
- package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-modify-view.component-DienTcPE.mjs.map +0 -1
- package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-single-view.component-DBUwSzvg.mjs.map +0 -1
- package/fesm2022/acorex-platform-framework-client-themes-shared-settings.provider-CpsAIsxj.mjs.map +0 -1
|
@@ -2,6 +2,7 @@ import * as i0 from '@angular/core';
|
|
|
2
2
|
import { InjectionToken, Injector, Injectable, signal, inject, Input, Directive, Optional, Inject, NgModule, input, output, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
3
|
import { map, BehaviorSubject, shareReplay, defaultIfEmpty, switchMap, filter, distinctUntilChanged, of, from, combineLatest, first } from 'rxjs';
|
|
4
4
|
import { AXPDebugService, AXPBroadcastEventService, AXP_SESSION_SERVICE, AXP_EXPRESSION_EVALUATOR_SCOPE_PROVIDER, provideAppStartUpTask, AXP_APP_STARTUP_PRIORITY } from '@acorex-platform/framework-client/core';
|
|
5
|
+
import { Router } from '@angular/router';
|
|
5
6
|
import { isEmpty } from 'lodash-es';
|
|
6
7
|
import { AXPCommandService, AXPQueryService } from '@acorex-platform/framework-client/runtime';
|
|
7
8
|
import { AUTH_LOGOUT_DEFAULT, AUTH_REFRESH_DEFAULT, AUTH_UPDATE_SESSION_DEFAULT, AUTH_LIST_OWN_DEVICE_SESSIONS_DEFAULT, AUTH_TERMINATE_OWN_DEVICE_SESSION_DEFAULT, AUTH_LIST_PROVIDERS_DEFAULT } from '@acorex-platform/framework-shared/runtime';
|
|
@@ -131,6 +132,10 @@ function traceAuthLifecycle(message, data) {
|
|
|
131
132
|
function traceAuthLifecycleError(message, data) {
|
|
132
133
|
AXPDebugService.instance?.error(AUTH_SESSION_TRACE_NS, AUTH_SESSION_TRACE_TITLE.Lifecycle, message, data);
|
|
133
134
|
}
|
|
135
|
+
/** Always emits (Debug Mode not required) — use for logout / empty-menu diagnosis. */
|
|
136
|
+
function warnAuthLifecycle(message, data) {
|
|
137
|
+
AXPDebugService.instance?.warn(AUTH_SESSION_TRACE_NS, AUTH_SESSION_TRACE_TITLE.Lifecycle, message, data);
|
|
138
|
+
}
|
|
134
139
|
//#endregion
|
|
135
140
|
//#region ----- Restore -----
|
|
136
141
|
function traceAuthRestore(message, data) {
|
|
@@ -141,11 +146,17 @@ function traceAuthRestore(message, data) {
|
|
|
141
146
|
function traceAuthWorkspace(message, data) {
|
|
142
147
|
AXPDebugService.instance?.trace(AUTH_SESSION_TRACE_NS, AUTH_SESSION_TRACE_TITLE.Workspace, message, data);
|
|
143
148
|
}
|
|
149
|
+
function warnAuthWorkspace(message, data) {
|
|
150
|
+
AXPDebugService.instance?.warn(AUTH_SESSION_TRACE_NS, AUTH_SESSION_TRACE_TITLE.Workspace, message, data);
|
|
151
|
+
}
|
|
144
152
|
//#endregion
|
|
145
153
|
//#region ----- Access policy -----
|
|
146
154
|
function traceAuthAccessPolicy(message, data) {
|
|
147
155
|
AXPDebugService.instance?.trace(AUTH_SESSION_TRACE_NS, AUTH_SESSION_TRACE_TITLE.AccessPolicy, message, data);
|
|
148
156
|
}
|
|
157
|
+
function warnAuthAccessPolicy(message, data) {
|
|
158
|
+
AXPDebugService.instance?.warn(AUTH_SESSION_TRACE_NS, AUTH_SESSION_TRACE_TITLE.AccessPolicy, message, data);
|
|
159
|
+
}
|
|
149
160
|
//#endregion
|
|
150
161
|
//#region ----- Catalog -----
|
|
151
162
|
function traceAuthCatalog(message, data) {
|
|
@@ -353,6 +364,87 @@ function isOAuthRedirectInFlight() {
|
|
|
353
364
|
}
|
|
354
365
|
//#endregion
|
|
355
366
|
|
|
367
|
+
//#region ---- Auth refresh lock (cross-tab) ----
|
|
368
|
+
/**
|
|
369
|
+
* Serializes cookie refresh across browser tabs so two restores cannot consume the
|
|
370
|
+
* same refresh `jti` and trigger server reuse detection (full `sid` revoke).
|
|
371
|
+
*
|
|
372
|
+
* 1. Web Locks API (Chrome / Edge / Firefox) — exclusive across tabs of this origin.
|
|
373
|
+
* 2. localStorage lock — fallback when Web Locks is missing (two JS heaps otherwise race).
|
|
374
|
+
* 3. In-tab promise chain — last resort (does not coordinate tabs).
|
|
375
|
+
*/
|
|
376
|
+
const AUTH_REFRESH_LOCK_NAME = 'axp.auth.refresh';
|
|
377
|
+
const AUTH_REFRESH_LS_LOCK_KEY = 'axp.auth.refresh.ls-lock';
|
|
378
|
+
const AUTH_REFRESH_LS_TTL_MS = 20_000;
|
|
379
|
+
const AUTH_REFRESH_LS_WAIT_MS = 40;
|
|
380
|
+
let memoryTail = Promise.resolve();
|
|
381
|
+
async function withAuthRefreshLock(run) {
|
|
382
|
+
const locks = typeof navigator !== 'undefined' ? navigator.locks : undefined;
|
|
383
|
+
if (typeof locks?.request === 'function') {
|
|
384
|
+
return locks.request(AUTH_REFRESH_LOCK_NAME, { mode: 'exclusive' }, async () => run());
|
|
385
|
+
}
|
|
386
|
+
if (typeof localStorage !== 'undefined') {
|
|
387
|
+
return withLocalStorageRefreshLock(run);
|
|
388
|
+
}
|
|
389
|
+
return withMemoryRefreshLock(run);
|
|
390
|
+
}
|
|
391
|
+
async function withLocalStorageRefreshLock(run) {
|
|
392
|
+
const owner = `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
393
|
+
const deadline = Date.now() + AUTH_REFRESH_LS_TTL_MS;
|
|
394
|
+
while (Date.now() < deadline) {
|
|
395
|
+
const now = Date.now();
|
|
396
|
+
const held = readLocalStorageLock();
|
|
397
|
+
if (held && held.until > now && held.owner !== owner) {
|
|
398
|
+
await delay(AUTH_REFRESH_LS_WAIT_MS);
|
|
399
|
+
continue;
|
|
400
|
+
}
|
|
401
|
+
writeLocalStorageLock(owner, now + AUTH_REFRESH_LS_TTL_MS);
|
|
402
|
+
const confirmed = readLocalStorageLock();
|
|
403
|
+
if (confirmed?.owner !== owner) {
|
|
404
|
+
await delay(AUTH_REFRESH_LS_WAIT_MS);
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
407
|
+
try {
|
|
408
|
+
return await run();
|
|
409
|
+
}
|
|
410
|
+
finally {
|
|
411
|
+
const current = readLocalStorageLock();
|
|
412
|
+
if (current?.owner === owner) {
|
|
413
|
+
localStorage.removeItem(AUTH_REFRESH_LS_LOCK_KEY);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
return withMemoryRefreshLock(run);
|
|
418
|
+
}
|
|
419
|
+
function readLocalStorageLock() {
|
|
420
|
+
try {
|
|
421
|
+
const raw = localStorage.getItem(AUTH_REFRESH_LS_LOCK_KEY);
|
|
422
|
+
if (!raw) {
|
|
423
|
+
return null;
|
|
424
|
+
}
|
|
425
|
+
const parsed = JSON.parse(raw);
|
|
426
|
+
if (typeof parsed.owner !== 'string' || typeof parsed.until !== 'number') {
|
|
427
|
+
return null;
|
|
428
|
+
}
|
|
429
|
+
return { owner: parsed.owner, until: parsed.until };
|
|
430
|
+
}
|
|
431
|
+
catch {
|
|
432
|
+
return null;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
function writeLocalStorageLock(owner, until) {
|
|
436
|
+
localStorage.setItem(AUTH_REFRESH_LS_LOCK_KEY, JSON.stringify({ owner, until }));
|
|
437
|
+
}
|
|
438
|
+
function delay(ms) {
|
|
439
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
440
|
+
}
|
|
441
|
+
function withMemoryRefreshLock(run) {
|
|
442
|
+
const runExclusive = memoryTail.then(() => run(), () => run());
|
|
443
|
+
memoryTail = runExclusive.then(() => undefined, () => undefined);
|
|
444
|
+
return runExclusive;
|
|
445
|
+
}
|
|
446
|
+
//#endregion
|
|
447
|
+
|
|
356
448
|
//#region ---- Max active device sessions (client) ----
|
|
357
449
|
/** Stable API / ForbiddenException code from AuthService. */
|
|
358
450
|
const MAX_ACTIVE_DEVICE_SESSIONS_CODE = 'MAX_ACTIVE_DEVICE_SESSIONS';
|
|
@@ -447,6 +539,7 @@ class AXPSessionService {
|
|
|
447
539
|
/** In-memory catalog caches — shared by guards and chooser UI (one List* per bind). */
|
|
448
540
|
this.tenantsListCache = null;
|
|
449
541
|
this.applicationsListCache = null;
|
|
542
|
+
this.signoutInFlight = null;
|
|
450
543
|
this.status = new BehaviorSubject(AXPSessionStatus.Unauthenticated);
|
|
451
544
|
this.status$ = this.status.asObservable().pipe(shareReplay(1));
|
|
452
545
|
// Add loading state to prevent premature redirects
|
|
@@ -581,23 +674,15 @@ class AXPSessionService {
|
|
|
581
674
|
applicationId: sessionData.application?.id,
|
|
582
675
|
strategy: strategyName,
|
|
583
676
|
}));
|
|
584
|
-
|
|
677
|
+
// Cross-tab exclusive refresh — avoids concurrent jti reuse / sid revoke.
|
|
678
|
+
const refreshResult = await this.refreshCookieSessionExclusive(strategyName, strategy);
|
|
585
679
|
if (!refreshResult?.succeed || !refreshResult.data?.user) {
|
|
586
680
|
this.clearSession();
|
|
587
681
|
this.status.next(AXPSessionStatus.Unauthorized);
|
|
588
682
|
traceAuthRestore('Refresh failed — cleared');
|
|
589
683
|
return;
|
|
590
684
|
}
|
|
591
|
-
|
|
592
|
-
const merged = {
|
|
593
|
-
succeed: true,
|
|
594
|
-
data: {
|
|
595
|
-
...refreshResult.data,
|
|
596
|
-
tenant: refreshResult.data.tenant ?? sessionData.tenant ?? null,
|
|
597
|
-
application: refreshResult.data.application ?? sessionData.application ?? null,
|
|
598
|
-
},
|
|
599
|
-
};
|
|
600
|
-
await this.applySignInResult(strategyName, merged);
|
|
685
|
+
await this.applySignInResult(strategyName, refreshResult);
|
|
601
686
|
traceAuthRestore('Restore complete', () => ({
|
|
602
687
|
ms: Date.now() - startedAt,
|
|
603
688
|
status: this.status.value,
|
|
@@ -745,7 +830,9 @@ class AXPSessionService {
|
|
|
745
830
|
if (!strategy) {
|
|
746
831
|
throw new Error(`Authentication strategy '${strategyName}' is not supported`);
|
|
747
832
|
}
|
|
748
|
-
|
|
833
|
+
// Same lock as cookie refresh — UpdateSession rotates refresh `jti`.
|
|
834
|
+
// A restore in another tab must not consume the previous cookie concurrently.
|
|
835
|
+
const result = await withAuthRefreshLock(() => strategy.updateToken(params));
|
|
749
836
|
if (result?.succeed) {
|
|
750
837
|
if (params?.['code']) {
|
|
751
838
|
this.clearSession();
|
|
@@ -784,6 +871,11 @@ class AXPSessionService {
|
|
|
784
871
|
traceAuthLifecycleError('UpdateSession failed', () => ({
|
|
785
872
|
error: error instanceof Error ? error.message : String(error),
|
|
786
873
|
}));
|
|
874
|
+
warnAuthWorkspace('UpdateSession failed — clearing session (logout UX)', () => ({
|
|
875
|
+
error: error instanceof Error ? error.message : String(error),
|
|
876
|
+
tenantId: params?.['tenant']?.id ?? params?.['tenantId'] ?? null,
|
|
877
|
+
applicationId: params?.['application']?.id ?? params?.['applicationId'] ?? null,
|
|
878
|
+
}));
|
|
787
879
|
// Failed workspace bind must not leave a half-authenticated cookie session
|
|
788
880
|
// (that causes splash restore loops after refresh).
|
|
789
881
|
// Exception: max device sessions — keep identity session so the user can
|
|
@@ -802,28 +894,51 @@ class AXPSessionService {
|
|
|
802
894
|
}
|
|
803
895
|
}
|
|
804
896
|
async signout() {
|
|
805
|
-
|
|
806
|
-
|
|
897
|
+
if (this.signoutInFlight) {
|
|
898
|
+
return this.signoutInFlight;
|
|
899
|
+
}
|
|
900
|
+
this.signoutInFlight = this.performSignout().finally(() => {
|
|
901
|
+
this.signoutInFlight = null;
|
|
902
|
+
});
|
|
903
|
+
return this.signoutInFlight;
|
|
904
|
+
}
|
|
905
|
+
/**
|
|
906
|
+
* Clears the client session first, then redirects, then revokes the server session.
|
|
907
|
+
* Waiting on the logout HTTP call (or flipping isLoading) used to skip the login
|
|
908
|
+
* redirect and let in-flight 401s start a competing navigation back into the app.
|
|
909
|
+
*/
|
|
910
|
+
async performSignout() {
|
|
911
|
+
const userId = this.user?.id;
|
|
912
|
+
const sessionData = this.getSessionData();
|
|
913
|
+
const strategy = sessionData?.strategy
|
|
914
|
+
? this.authStrategyRegistry.get(sessionData.strategy)
|
|
915
|
+
: null;
|
|
916
|
+
traceAuthLifecycle('Sign-out started', () => ({ userId }));
|
|
917
|
+
this.clearSession();
|
|
918
|
+
this.eventService.publish(AXPSessionStatus.SignedOut, { id: userId });
|
|
919
|
+
this.status.next(AXPSessionStatus.SignedOut);
|
|
920
|
+
this.navigateToLoginAfterSignout();
|
|
921
|
+
if (strategy) {
|
|
922
|
+
try {
|
|
923
|
+
await strategy.signout();
|
|
924
|
+
}
|
|
925
|
+
catch {
|
|
926
|
+
// Ignore logout network errors; client session is already cleared.
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
traceAuthLifecycle('Sign-out complete', () => ({ userId }));
|
|
930
|
+
}
|
|
931
|
+
navigateToLoginAfterSignout() {
|
|
807
932
|
try {
|
|
808
|
-
const
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
try {
|
|
813
|
-
await strategy.signout();
|
|
814
|
-
}
|
|
815
|
-
catch (error) { }
|
|
816
|
-
}
|
|
933
|
+
const router = this.injector.get(Router);
|
|
934
|
+
const path = (router.url ?? '').split('?')[0];
|
|
935
|
+
if (path.startsWith('/auth')) {
|
|
936
|
+
return;
|
|
817
937
|
}
|
|
818
|
-
|
|
819
|
-
this.clearSession();
|
|
820
|
-
this.eventService.publish(AXPSessionStatus.SignedOut, { id: userId });
|
|
821
|
-
this.isLoading.next(false);
|
|
822
|
-
this.status.next(AXPSessionStatus.SignedOut);
|
|
823
|
-
traceAuthLifecycle('Sign-out complete', () => ({ userId }));
|
|
938
|
+
void router.navigateByUrl('/auth/login', { replaceUrl: true });
|
|
824
939
|
}
|
|
825
|
-
|
|
826
|
-
|
|
940
|
+
catch {
|
|
941
|
+
// Router is unavailable in some test / early-boot hosts.
|
|
827
942
|
}
|
|
828
943
|
}
|
|
829
944
|
async refreshToken() {
|
|
@@ -831,34 +946,74 @@ class AXPSessionService {
|
|
|
831
946
|
if (!sessionData?.strategy || !sessionData.cookieSession) {
|
|
832
947
|
throw new Error('No cookie session available');
|
|
833
948
|
}
|
|
834
|
-
const
|
|
949
|
+
const strategyName = sessionData.strategy;
|
|
950
|
+
const strategy = this.authStrategyRegistry.get(strategyName);
|
|
835
951
|
if (!strategy) {
|
|
836
|
-
throw new Error(`Authentication strategy '${
|
|
952
|
+
throw new Error(`Authentication strategy '${strategyName}' is not found`);
|
|
837
953
|
}
|
|
838
954
|
try {
|
|
839
|
-
const result = await
|
|
955
|
+
const result = await this.refreshCookieSessionExclusive(strategyName, strategy);
|
|
840
956
|
if (!result.succeed || !result.data?.user) {
|
|
957
|
+
warnAuthLifecycle('Token refresh failed — clearing session', () => ({
|
|
958
|
+
strategy: strategyName,
|
|
959
|
+
}));
|
|
841
960
|
this.clearSession();
|
|
842
961
|
this.status.next(AXPSessionStatus.Expired);
|
|
843
962
|
throw new Error('Token refresh failed');
|
|
844
963
|
}
|
|
845
|
-
this.setSession({
|
|
846
|
-
strategy: sessionData.strategy,
|
|
847
|
-
user: result.data.user,
|
|
848
|
-
tenant: result.data.tenant ?? sessionData.tenant ?? null,
|
|
849
|
-
application: result.data.application ?? sessionData.application ?? null,
|
|
850
|
-
expiresIn: result.data.expiresIn,
|
|
851
|
-
cookieSession: true,
|
|
852
|
-
locked: sessionData.locked === true,
|
|
853
|
-
});
|
|
854
964
|
return true;
|
|
855
965
|
}
|
|
856
966
|
catch (error) {
|
|
967
|
+
warnAuthLifecycle('Token refresh error — clearing session', () => ({
|
|
968
|
+
error: error instanceof Error ? error.message : String(error),
|
|
969
|
+
}));
|
|
857
970
|
this.clearSession();
|
|
858
971
|
this.status.next(AXPSessionStatus.Expired);
|
|
859
972
|
throw error;
|
|
860
973
|
}
|
|
861
974
|
}
|
|
975
|
+
/**
|
|
976
|
+
* One refresh at a time across tabs (Web Locks). Re-reads and writes session
|
|
977
|
+
* metadata inside the lock so a peer tab never replays the same refresh `jti`.
|
|
978
|
+
*/
|
|
979
|
+
async refreshCookieSessionExclusive(strategyName, strategy) {
|
|
980
|
+
return withAuthRefreshLock(async () => {
|
|
981
|
+
const latest = this.getSessionData();
|
|
982
|
+
if (!latest?.user || !latest.cookieSession) {
|
|
983
|
+
return { succeed: false };
|
|
984
|
+
}
|
|
985
|
+
const activeStrategyName = latest.strategy?.trim() || strategyName;
|
|
986
|
+
const activeStrategy = activeStrategyName === strategyName
|
|
987
|
+
? strategy
|
|
988
|
+
: this.authStrategyRegistry.get(activeStrategyName);
|
|
989
|
+
if (!activeStrategy) {
|
|
990
|
+
return { succeed: false };
|
|
991
|
+
}
|
|
992
|
+
const refreshResult = await activeStrategy.refreshToken(this.getContext());
|
|
993
|
+
if (!refreshResult?.succeed || !refreshResult.data?.user) {
|
|
994
|
+
return { succeed: false };
|
|
995
|
+
}
|
|
996
|
+
const merged = {
|
|
997
|
+
succeed: true,
|
|
998
|
+
data: {
|
|
999
|
+
...refreshResult.data,
|
|
1000
|
+
tenant: refreshResult.data.tenant ?? latest.tenant ?? null,
|
|
1001
|
+
application: refreshResult.data.application ?? latest.application ?? null,
|
|
1002
|
+
},
|
|
1003
|
+
};
|
|
1004
|
+
// Persist before releasing the lock so the next tab reads updated metadata.
|
|
1005
|
+
this.setSession({
|
|
1006
|
+
strategy: activeStrategyName,
|
|
1007
|
+
user: merged.data.user,
|
|
1008
|
+
tenant: merged.data.tenant ?? null,
|
|
1009
|
+
application: merged.data.application ?? null,
|
|
1010
|
+
expiresIn: merged.data.expiresIn,
|
|
1011
|
+
cookieSession: true,
|
|
1012
|
+
locked: latest.locked === true,
|
|
1013
|
+
});
|
|
1014
|
+
return merged;
|
|
1015
|
+
});
|
|
1016
|
+
}
|
|
862
1017
|
/**
|
|
863
1018
|
* Auth:GetAccessPolicy is only meaningful with tenant + application bound.
|
|
864
1019
|
* Skipping incomplete contexts cuts 2 redundant calls on the common login path.
|
|
@@ -885,6 +1040,14 @@ class AXPSessionService {
|
|
|
885
1040
|
tenantId: this.tenant?.id,
|
|
886
1041
|
applicationId: this.application?.id,
|
|
887
1042
|
}));
|
|
1043
|
+
if (reason === 'updateToken' && this.permissions.length === 0) {
|
|
1044
|
+
warnAuthAccessPolicy('GetAccessPolicy returned empty permissions after workspace bind', () => ({
|
|
1045
|
+
reason,
|
|
1046
|
+
tenantId: this.tenant?.id ?? null,
|
|
1047
|
+
applicationId: this.application?.id ?? null,
|
|
1048
|
+
featureCount: this.features.length,
|
|
1049
|
+
}));
|
|
1050
|
+
}
|
|
888
1051
|
}
|
|
889
1052
|
async loadPermissions() {
|
|
890
1053
|
try {
|
|
@@ -895,6 +1058,11 @@ class AXPSessionService {
|
|
|
895
1058
|
traceAuthLifecycleError('Load permissions failed', () => ({
|
|
896
1059
|
error: error instanceof Error ? error.message : String(error),
|
|
897
1060
|
}));
|
|
1061
|
+
warnAuthLifecycle('Load permissions failed — menus may be empty', () => ({
|
|
1062
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1063
|
+
tenantId: this.tenant?.id ?? null,
|
|
1064
|
+
applicationId: this.application?.id ?? null,
|
|
1065
|
+
}));
|
|
898
1066
|
this.permissionsSubject.next([]);
|
|
899
1067
|
}
|
|
900
1068
|
}
|
|
@@ -2327,5 +2495,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
|
|
|
2327
2495
|
* Generated bundle index. Do not edit.
|
|
2328
2496
|
*/
|
|
2329
2497
|
|
|
2330
|
-
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_PKCE_VERIFIER_KEY, AXP_PERMISSION_CHECKER, AXP_PERMISSION_DEFINITION_PROVIDER, AXP_PERMISSION_LOADER, AXP_PERMISSION_MODULES_RESOLVER, AXP_PLATFORM_OAUTH_CSRF_COOKIE, AXP_TENANT_LOADER, AXP_USER_AVATAR_CACHE_PORT, JwtUtil, MAX_ACTIVE_DEVICE_SESSIONS_CODE, PkceUtil, TimeUtil, asAuthListProvidersResponse, clearOAuthPendingStrategy, clearSessionLimitPendingTenant, commandMessageTextForError, exchangePlatformOAuthAuthorizationCode, fetchPlatformOAuthDiscoveryDocument, findIdentityProvider, getOAuthPendingStrategy, hasLegacyOAuthPkceVerifier, hasOAuthPendingFlow, initPlatformOAuthAuthorization, isMaxActiveDeviceSessionsError, isOAuthRedirectInFlight, platformOAuthSessionUpdateParams, readPlatformOAuthCsrfToken, readSessionLimitPendingTenant, resolvePlatformOAuthAuthorizeEndpoint, resolvePlatformOAuthClientConfig, resolvePlatformOAuthIssuer, resolvePlatformOAuthRedirectUri, resolvePlatformOAuthTokenEndpoint, setOAuthPendingStrategy, shouldPlatformOAuthBindSession, storeSessionLimitPendingTenant, toPlatformOAuthSignInResult, traceAuthAccessPolicy, traceAuthCatalog, traceAuthGuard, traceAuthLifecycle, traceAuthLifecycleError, traceAuthRestore, traceAuthWorkspace };
|
|
2498
|
+
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_PKCE_VERIFIER_KEY, AXP_PERMISSION_CHECKER, AXP_PERMISSION_DEFINITION_PROVIDER, AXP_PERMISSION_LOADER, AXP_PERMISSION_MODULES_RESOLVER, AXP_PLATFORM_OAUTH_CSRF_COOKIE, AXP_TENANT_LOADER, AXP_USER_AVATAR_CACHE_PORT, JwtUtil, MAX_ACTIVE_DEVICE_SESSIONS_CODE, PkceUtil, TimeUtil, asAuthListProvidersResponse, clearOAuthPendingStrategy, clearSessionLimitPendingTenant, commandMessageTextForError, exchangePlatformOAuthAuthorizationCode, fetchPlatformOAuthDiscoveryDocument, findIdentityProvider, getOAuthPendingStrategy, hasLegacyOAuthPkceVerifier, hasOAuthPendingFlow, initPlatformOAuthAuthorization, isMaxActiveDeviceSessionsError, isOAuthRedirectInFlight, platformOAuthSessionUpdateParams, readPlatformOAuthCsrfToken, readSessionLimitPendingTenant, resolvePlatformOAuthAuthorizeEndpoint, resolvePlatformOAuthClientConfig, resolvePlatformOAuthIssuer, resolvePlatformOAuthRedirectUri, resolvePlatformOAuthTokenEndpoint, setOAuthPendingStrategy, shouldPlatformOAuthBindSession, storeSessionLimitPendingTenant, toPlatformOAuthSignInResult, traceAuthAccessPolicy, traceAuthCatalog, traceAuthGuard, traceAuthLifecycle, traceAuthLifecycleError, traceAuthRestore, traceAuthWorkspace, warnAuthAccessPolicy, warnAuthLifecycle, warnAuthWorkspace };
|
|
2331
2499
|
//# sourceMappingURL=acorex-platform-framework-client-auth.mjs.map
|