@arsedizioni/ars-utils 22.5.15 → 22.5.16
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/arsedizioni-ars-utils-clipper.common.mjs +82 -23
- package/fesm2022/arsedizioni-ars-utils-clipper.common.mjs.map +1 -1
- package/fesm2022/arsedizioni-ars-utils-evolution.common.mjs +77 -22
- package/fesm2022/arsedizioni-ars-utils-evolution.common.mjs.map +1 -1
- package/package.json +1 -1
- package/types/arsedizioni-ars-utils-clipper.common.d.ts +42 -10
- package/types/arsedizioni-ars-utils-evolution.common.d.ts +39 -11
|
@@ -2185,6 +2185,19 @@ function isAuthEndpoint(url) {
|
|
|
2185
2185
|
function isSessionExpired(errorStatus, url) {
|
|
2186
2186
|
return errorStatus === 401 && !isAuthEndpoint(url);
|
|
2187
2187
|
}
|
|
2188
|
+
/**
|
|
2189
|
+
* Returns whether the URL is the silent link probe, whose failures must never reach the user.
|
|
2190
|
+
*
|
|
2191
|
+
* `/session/status` runs unconditionally at every bootstrap, including for users who never linked
|
|
2192
|
+
* Clipper at all. When the API is unreachable, reporting that failure would show "Clipper non
|
|
2193
|
+
* disponibile" to somebody who never asked Clipper for anything: the probe simply stays unanswered
|
|
2194
|
+
* and the link state keeps whatever the stored hint says.
|
|
2195
|
+
* @param url - The request URL.
|
|
2196
|
+
* @returns True when the URL is the status probe.
|
|
2197
|
+
*/
|
|
2198
|
+
function isStatusProbe(url) {
|
|
2199
|
+
return url.includes('/session/status');
|
|
2200
|
+
}
|
|
2188
2201
|
/**
|
|
2189
2202
|
* Resolves a user-facing error message based on the HTTP status code.
|
|
2190
2203
|
*
|
|
@@ -2331,7 +2344,11 @@ function clipperAuthInterceptor(serviceUri, flags = ClipperServiceFlags.None) {
|
|
|
2331
2344
|
// at all. Everything that gets here is a Clipper request anyway: the others returned
|
|
2332
2345
|
// before the pipe was built.
|
|
2333
2346
|
const errorStatus = parseInt(error?.status ?? '0');
|
|
2334
|
-
if (
|
|
2347
|
+
if (isStatusProbe(authenticatedRequest.url)) {
|
|
2348
|
+
// The silent probe stays silent: its caller reads the outcome from the stream, nobody
|
|
2349
|
+
// else needs to hear about it.
|
|
2350
|
+
}
|
|
2351
|
+
else if (isServiceUnavailable(error)) {
|
|
2335
2352
|
// Three attempts over ten seconds and the API is still not answering. There is nothing
|
|
2336
2353
|
// left to retry, so the host application is told once, however many requests were in
|
|
2337
2354
|
// flight.
|
|
@@ -2392,12 +2409,17 @@ class ClipperLoginService {
|
|
|
2392
2409
|
// BOOTSTRAP
|
|
2393
2410
|
////
|
|
2394
2411
|
/**
|
|
2395
|
-
*
|
|
2412
|
+
* Establishes the session state at bootstrap by asking the API — the only party that can read the
|
|
2413
|
+
* HttpOnly cookie — whether this browser still holds one.
|
|
2396
2414
|
*
|
|
2397
2415
|
* Called by `ClipperService.initialize`, right after `ClipperCoreService.initialize`, so a
|
|
2398
|
-
* consumer gets the restore for free and never has to know this method exists.
|
|
2399
|
-
*
|
|
2400
|
-
*
|
|
2416
|
+
* consumer gets the restore for free and never has to know this method exists.
|
|
2417
|
+
*
|
|
2418
|
+
* The probe now runs UNCONDITIONALLY, where it used to run only when a stored context existed.
|
|
2419
|
+
* The stored context is just a hint — "this browser signed in at some point" — and the cookie can
|
|
2420
|
+
* outlive it: local storage cleared while the month-long cookie stayed put made this library
|
|
2421
|
+
* declare "no link" with a perfectly valid session in the browser. The API is the only source of
|
|
2422
|
+
* truth on the matter, so it is asked every time.
|
|
2401
2423
|
*
|
|
2402
2424
|
* A host application that drives its own return from an external provider, or that starts on its
|
|
2403
2425
|
* own sign-in page, should call `core.setLoggedIn(false)` before bootstrapping rather than let
|
|
@@ -2408,30 +2430,53 @@ class ClipperLoginService {
|
|
|
2408
2430
|
if (this.initialized)
|
|
2409
2431
|
return;
|
|
2410
2432
|
this.initialized = true;
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
this.restoreSession();
|
|
2414
|
-
}
|
|
2433
|
+
this.core.loggingIn.set(true);
|
|
2434
|
+
this.restoreSession();
|
|
2415
2435
|
}
|
|
2416
2436
|
/**
|
|
2417
|
-
* Asks `/session/
|
|
2418
|
-
*
|
|
2437
|
+
* Asks `/session/status` whether this browser holds a live session, and hydrates the context
|
|
2438
|
+
* through {@link me} when it does.
|
|
2419
2439
|
*
|
|
2420
|
-
*
|
|
2421
|
-
*
|
|
2422
|
-
*
|
|
2440
|
+
* The probe is silent by construction: `/session/status` never answers 401, so a visitor who
|
|
2441
|
+
* never signed in — or a cookie whose session has died — comes back as `connected: false` with no
|
|
2442
|
+
* error broadcast and no dialog. Only then is the stale stored context cleared, so the `loggedIn`
|
|
2443
|
+
* signal tells the truth at every bootstrap.
|
|
2444
|
+
*
|
|
2445
|
+
* Nothing is cleared on a transport failure, and that is deliberate: an unreachable API is not
|
|
2446
|
+
* proof of a dead session — the proxy may have answered 502, the device may be offline — and
|
|
2447
|
+
* clearing on any of those would sign the user out with a perfectly valid cookie.
|
|
2423
2448
|
* @returns void
|
|
2424
2449
|
*/
|
|
2425
2450
|
restoreSession() {
|
|
2426
|
-
|
|
2427
|
-
|
|
2451
|
+
// Whether the probe handed over to `/session/me`, which then owns lowering `loggingIn`.
|
|
2452
|
+
let restoring = false;
|
|
2453
|
+
this.status()
|
|
2454
|
+
.pipe(finalize(() => { if (!restoring) {
|
|
2455
|
+
this.core.loggingIn.set(false);
|
|
2456
|
+
} }))
|
|
2428
2457
|
.subscribe({
|
|
2429
2458
|
next: r => {
|
|
2430
|
-
if (r?.success) {
|
|
2431
|
-
|
|
2459
|
+
if (r?.success && r.value?.connected) {
|
|
2460
|
+
restoring = true;
|
|
2461
|
+
this.me()
|
|
2462
|
+
.pipe(finalize(() => this.core.loggingIn.set(false)))
|
|
2463
|
+
.subscribe({
|
|
2464
|
+
next: m => {
|
|
2465
|
+
if (m?.success) {
|
|
2466
|
+
this.broadcastService.sendMessage(ClipperMessages.LOGIN_COMPLETED);
|
|
2467
|
+
}
|
|
2468
|
+
},
|
|
2469
|
+
// Consumed so the rethrown error is not reported a second time as an unhandled one.
|
|
2470
|
+
error: () => { }
|
|
2471
|
+
});
|
|
2472
|
+
}
|
|
2473
|
+
else if (this.core.loggedIn()) {
|
|
2474
|
+
// The stored context outlived its session: drop it, or the next bootstrap would keep
|
|
2475
|
+
// claiming a session that is gone.
|
|
2476
|
+
this.core.clear(true);
|
|
2432
2477
|
}
|
|
2433
2478
|
},
|
|
2434
|
-
//
|
|
2479
|
+
// Transport failure: keep the stored hint, the next bootstrap will ask again.
|
|
2435
2480
|
error: () => { }
|
|
2436
2481
|
});
|
|
2437
2482
|
}
|
|
@@ -2496,6 +2541,18 @@ class ClipperLoginService {
|
|
|
2496
2541
|
.post(this.core.serviceUri + '/session/logout', {})
|
|
2497
2542
|
.pipe(finalize(() => this.core.clear(true)), catchError(() => of({ success: false })));
|
|
2498
2543
|
}
|
|
2544
|
+
/**
|
|
2545
|
+
* Asks the API whether this browser holds a live session, without ever failing with a 401: the
|
|
2546
|
+
* route is anonymous and answers `connected: false` to a caller with no session. This is the one
|
|
2547
|
+
* honest way to know the link state — the auth cookie is HttpOnly and belongs to the API's host,
|
|
2548
|
+
* so it cannot be read from here, and its presence would prove nothing anyway, since the session
|
|
2549
|
+
* also lives server-side and may have been revoked or evicted.
|
|
2550
|
+
* @returns An observable emitting the API result wrapping the session status.
|
|
2551
|
+
*/
|
|
2552
|
+
status() {
|
|
2553
|
+
return this.httpClient
|
|
2554
|
+
.get(this.core.serviceUri + '/session/status');
|
|
2555
|
+
}
|
|
2499
2556
|
/**
|
|
2500
2557
|
* Reads the current session from the server, which resolves it from the auth cookie claims
|
|
2501
2558
|
* without touching the database. Used to restore the state after a page refresh instead of
|
|
@@ -3313,10 +3370,12 @@ class ClipperService {
|
|
|
3313
3370
|
*/
|
|
3314
3371
|
initialize(serviceUri, appUri, flags = ClipperServiceFlags.None) {
|
|
3315
3372
|
this.core.initialize(serviceUri, appUri, flags);
|
|
3316
|
-
// Second, and in this order: the core sets the base URI that `/session/
|
|
3317
|
-
// it no longer declares a session from what local storage
|
|
3318
|
-
//
|
|
3319
|
-
//
|
|
3373
|
+
// Second, and in this order: the core sets the base URI that `/session/status` and
|
|
3374
|
+
// `/session/me` are built from, and it no longer declares a session from what local storage
|
|
3375
|
+
// holds. Asking the API whether a session actually exists is what
|
|
3376
|
+
// `ClipperLoginService.initialize` does — unconditionally, since the HttpOnly cookie can
|
|
3377
|
+
// outlive any local hint — and doing it from here means a consumer keeps calling one method to
|
|
3378
|
+
// bootstrap, exactly as before.
|
|
3320
3379
|
this.session.initialize();
|
|
3321
3380
|
}
|
|
3322
3381
|
/**
|