@tidecloak/js 0.13.33-staging → 0.13.34-staging
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/dist/cjs/lib/tidecloak.js +226 -44
- package/dist/cjs/lib/tidecloak.js.map +1 -1
- package/dist/cjs/src/IAMService.js +61 -5
- package/dist/cjs/src/IAMService.js.map +1 -1
- package/dist/cjs/src/utils/index.js +4 -1
- package/dist/cjs/src/utils/index.js.map +1 -1
- package/dist/cjs/src/utils/silentCheckSso.js +138 -0
- package/dist/cjs/src/utils/silentCheckSso.js.map +1 -0
- package/dist/esm/lib/tidecloak.js +226 -44
- package/dist/esm/lib/tidecloak.js.map +1 -1
- package/dist/esm/src/IAMService.js +62 -6
- package/dist/esm/src/IAMService.js.map +1 -1
- package/dist/esm/src/utils/index.js +1 -0
- package/dist/esm/src/utils/index.js.map +1 -1
- package/dist/esm/src/utils/silentCheckSso.js +134 -0
- package/dist/esm/src/utils/silentCheckSso.js.map +1 -0
- package/dist/types/lib/tidecloak.d.ts +9 -0
- package/dist/types/src/utils/index.d.ts +1 -0
- package/dist/types/src/utils/silentCheckSso.d.ts +82 -0
- package/dist/types/utils/index.d.ts +1 -0
- package/dist/types/utils/silentCheckSso.d.ts +82 -0
- package/package.json +5 -4
|
@@ -70,12 +70,20 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (
|
|
|
70
70
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
71
71
|
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
72
72
|
};
|
|
73
|
-
var _TideCloak_instances, _TideCloak_refreshQueue, _TideCloak_adapter, _TideCloak_useNonce, _TideCloak_callbackStorage, _TideCloak_logInfo, _TideCloak_logWarn, _TideCloak_loginIframe, _TideCloak_dpopProvider, _TideCloak_config, _TideCloak_loadAdapter, _TideCloak_loadDefaultAdapter, _TideCloak_loadCordovaAdapter, _TideCloak_loadCordovaNativeAdapter, _TideCloak_loadConfig, _TideCloak_setupEndpoints, _TideCloak_loadOidcConfig, _TideCloak_setupOidcEndpoints, _TideCloak_getIssuerUrl, _TideCloak_check3pCookiesSupported, _TideCloak_processInit, _TideCloak_setupCheckLoginIframe, _TideCloak_checkLoginIframe, _TideCloak_checkSsoSilently, _TideCloak_parseCallback, _TideCloak_parseCallbackUrl, _TideCloak_parseCallbackParams, _TideCloak_processCallback, _TideCloak_scheduleCheckIframe, _TideCloak_fetchAccessToken, _TideCloak_fetchRefreshToken, _TideCloak_ensureRequestEnclaveOpen, _TideCloak_getVoucherUrl, _TideCloak_setToken, _TideCloak_getRealmUrl, _TideCloak_createLogger, _LocalStorage_instances, _LocalStorage_clearInvalidValues, _LocalStorage_clearAllValues, _LocalStorage_getStoredEntries, _LocalStorage_parseExpiry, _CookieStorage_instances, _CookieStorage_getCookie, _CookieStorage_setCookie, _CookieStorage_cookieExpiration;
|
|
73
|
+
var _TideCloak_instances, _TideCloak_refreshQueue, _TideCloak_adapter, _TideCloak_useNonce, _TideCloak_callbackStorage, _TideCloak_logInfo, _TideCloak_logWarn, _TideCloak_loginIframe, _TideCloak_dpopProvider, _TideCloak_config, _TideCloak_loadAdapter, _TideCloak_loadDefaultAdapter, _TideCloak_loadCordovaAdapter, _TideCloak_loadCordovaNativeAdapter, _TideCloak_loadConfig, _TideCloak_setupEndpoints, _TideCloak_loadOidcConfig, _TideCloak_setupOidcEndpoints, _TideCloak_getIssuerUrl, _TideCloak_check3pCookiesSupported, _TideCloak_processInit, _TideCloak_setupCheckLoginIframe, _TideCloak_checkLoginIframe, _TideCloak_checkSsoSilently, _TideCloak_parseCallback, _TideCloak_parseCallbackUrl, _TideCloak_parseCallbackParams, _TideCloak_processCallback, _TideCloak_scheduleCheckIframe, _TideCloak_consumePostLogoutMarker, _TideCloak_fetchAccessToken, _TideCloak_fetchRefreshToken, _TideCloak_ensureRequestEnclaveOpen, _TideCloak_reloginInteractively, _TideCloak_provideDokenOrRelogin, _TideCloak_getVoucherUrl, _TideCloak_setToken, _TideCloak_teardownEnclaves, _TideCloak_getRealmUrl, _TideCloak_createLogger, _LocalStorage_instances, _LocalStorage_clearInvalidValues, _LocalStorage_clearAllValues, _LocalStorage_getStoredEntries, _LocalStorage_parseExpiry, _CookieStorage_instances, _CookieStorage_getCookie, _CookieStorage_setCookie, _CookieStorage_cookieExpiration;
|
|
74
74
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
75
75
|
exports.NetworkError = exports.Models = exports.Tools = exports.PolicySignRequest = exports.ApprovalEnclaveNew = exports.ApprovalEnclave = exports.RequestEnclave = void 0;
|
|
76
76
|
// MODIFIED: Added dependency to external Tide helper libraries.
|
|
77
77
|
const heimdall_tide_1 = require("heimdall-tide");
|
|
78
78
|
const CONTENT_TYPE_JSON = 'application/json';
|
|
79
|
+
// Post-logout marker. After an explicit logout there is no session to silently
|
|
80
|
+
// resume, so the immediately-following init() must skip the racy silent
|
|
81
|
+
// check-sso (whose hidden iframe can net::ERR_ABORTED and wedge the app) and go
|
|
82
|
+
// straight to interactive login. `logout()` stamps this durable localStorage
|
|
83
|
+
// marker (survives the logout redirect round-trip); init() consumes it exactly
|
|
84
|
+
// once and forces an interactive login when present + fresh.
|
|
85
|
+
const POST_LOGOUT_MARKER_KEY = 'tide-post-logout';
|
|
86
|
+
const POST_LOGOUT_MARKER_TTL_MS = 60000;
|
|
79
87
|
/**
|
|
80
88
|
* @typedef {Object} Endpoints
|
|
81
89
|
* @property {() => string} authorize
|
|
@@ -149,6 +157,15 @@ class TideCloak {
|
|
|
149
157
|
this.timeSkew = null;
|
|
150
158
|
/** @type {boolean} */
|
|
151
159
|
this.silentCheckSsoFallback = true;
|
|
160
|
+
/**
|
|
161
|
+
* Max time (ms) to wait for the hidden silent-check-sso iframe to post back
|
|
162
|
+
* before treating the attempt as "not authenticated". Guards against the
|
|
163
|
+
* post-logout wedge where the check-sso (or the Tide enclave it spins up)
|
|
164
|
+
* never responds and init() would otherwise hang forever. Overridable via
|
|
165
|
+
* initOptions.silentCheckSsoTimeout.
|
|
166
|
+
* @type {number}
|
|
167
|
+
*/
|
|
168
|
+
this.silentCheckSsoTimeout = 10000;
|
|
152
169
|
/** @type {TideCloakPkceMethod} */
|
|
153
170
|
this.pkceMethod = 'S256';
|
|
154
171
|
this.enableLogging = false;
|
|
@@ -227,6 +244,9 @@ class TideCloak {
|
|
|
227
244
|
if (typeof initOptions.silentCheckSsoFallback === 'boolean') {
|
|
228
245
|
this.silentCheckSsoFallback = initOptions.silentCheckSsoFallback;
|
|
229
246
|
}
|
|
247
|
+
if (typeof initOptions.silentCheckSsoTimeout === 'number' && initOptions.silentCheckSsoTimeout > 0) {
|
|
248
|
+
this.silentCheckSsoTimeout = initOptions.silentCheckSsoTimeout;
|
|
249
|
+
}
|
|
230
250
|
if (typeof initOptions.pkceMethod !== 'undefined') {
|
|
231
251
|
if (initOptions.pkceMethod !== 'S256' && initOptions.pkceMethod !== false) {
|
|
232
252
|
throw new TypeError(`Invalid value for pkceMethod', expected 'S256' or false but got ${initOptions.pkceMethod}.`);
|
|
@@ -273,9 +293,29 @@ class TideCloak {
|
|
|
273
293
|
await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_check3pCookiesSupported).call(this);
|
|
274
294
|
await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_processInit).call(this, initOptions);
|
|
275
295
|
(_c = this.onReady) === null || _c === void 0 ? void 0 : _c.call(this, this.authenticated);
|
|
276
|
-
//
|
|
277
|
-
|
|
278
|
-
|
|
296
|
+
// Do NOT eagerly open the Tide RequestEnclave during init().
|
|
297
|
+
//
|
|
298
|
+
// On a post-logout silent re-auth, the doken returned by the silent
|
|
299
|
+
// check-sso can be STALE: its session key no longer matches the session the
|
|
300
|
+
// hidden enclave cached from the previous login. Opening the hidden enclave
|
|
301
|
+
// here makes it sit "waiting for doken refresh" forever - a refresh that
|
|
302
|
+
// never comes because no fresh doken exists - which is exactly the
|
|
303
|
+
// "[ENCLAVE] Received init but waiting for doken refresh" ... hang that
|
|
304
|
+
// wedges the SPA on "Signing you in..." on the second login. (The enclave
|
|
305
|
+
// silently WAITS rather than emitting an error, so heimdall's own
|
|
306
|
+
// requireReloginCallback recovery never fires.)
|
|
307
|
+
//
|
|
308
|
+
// Instead we DEFER enclave setup: it is created lazily on the first real
|
|
309
|
+
// Tide operation (encrypt / decrypt / approve / signDpopApproval - each
|
|
310
|
+
// calls initRequestEnclave()), by which point a fresh interactive login has
|
|
311
|
+
// minted a current doken and the enclave can complete its handshake. We
|
|
312
|
+
// still register the user-gesture listener so the popup fallback can open
|
|
313
|
+
// once an enclave has actually been created; #ensureRequestEnclaveOpen
|
|
314
|
+
// no-ops while there is no enclave or no doken.
|
|
315
|
+
if (initOptions.setupRequestEnclave) {
|
|
316
|
+
if (this.doken) {
|
|
317
|
+
__classPrivateFieldGet(this, _TideCloak_logInfo, "f").call(this, '[TIDECLOAK] Deferring Tide RequestEnclave setup until first use (not opening during init to avoid a stale-doken handshake stall on silent re-auth).');
|
|
318
|
+
}
|
|
279
319
|
// to get around popups requiring user gestures
|
|
280
320
|
document.addEventListener('click', () => {
|
|
281
321
|
__classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_ensureRequestEnclaveOpen).call(this);
|
|
@@ -379,6 +419,16 @@ class TideCloak {
|
|
|
379
419
|
this.logout = async (options) => {
|
|
380
420
|
var _a;
|
|
381
421
|
await ((_a = __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f")) === null || _a === void 0 ? void 0 : _a.flush());
|
|
422
|
+
// Stamp the post-logout marker so the next init() forces interactive login
|
|
423
|
+
// instead of running the racy silent check-sso. See POST_LOGOUT_MARKER_KEY.
|
|
424
|
+
try {
|
|
425
|
+
if (typeof window !== 'undefined' && window.localStorage) {
|
|
426
|
+
window.localStorage.setItem(POST_LOGOUT_MARKER_KEY, Date.now().toString());
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
catch (e) {
|
|
430
|
+
__classPrivateFieldGet(this, _TideCloak_logWarn, "f").call(this, '[TIDE-POSTLOGOUT] could not set marker: ' + (e instanceof Error ? e.message : String(e)));
|
|
431
|
+
}
|
|
382
432
|
return __classPrivateFieldGet(this, _TideCloak_adapter, "f").logout(options);
|
|
383
433
|
};
|
|
384
434
|
/**
|
|
@@ -667,19 +717,8 @@ class TideCloak {
|
|
|
667
717
|
isRunningLocal: new URL(__classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_getVoucherUrl).call(this)).hostname === "localhost"
|
|
668
718
|
}).init({
|
|
669
719
|
doken: this.doken,
|
|
670
|
-
dokenRefreshCallback: async () =>
|
|
671
|
-
|
|
672
|
-
if (!this.doken)
|
|
673
|
-
throw new Error('[TIDECLOAK] No doken found');
|
|
674
|
-
return this.doken;
|
|
675
|
-
},
|
|
676
|
-
requireReloginCallback: async () => {
|
|
677
|
-
await this.login({
|
|
678
|
-
idpHint: 'tide',
|
|
679
|
-
prompt: 'login',
|
|
680
|
-
redirectUri: window.location.href
|
|
681
|
-
});
|
|
682
|
-
}
|
|
720
|
+
dokenRefreshCallback: async () => __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_provideDokenOrRelogin).call(this),
|
|
721
|
+
requireReloginCallback: async () => __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_reloginInteractively).call(this)
|
|
683
722
|
});
|
|
684
723
|
}
|
|
685
724
|
}
|
|
@@ -701,19 +740,8 @@ class TideCloak {
|
|
|
701
740
|
backgroundUrl: __classPrivateFieldGet(this, _TideCloak_config, "f")['backgroundUrl'],
|
|
702
741
|
logoUrl: __classPrivateFieldGet(this, _TideCloak_config, "f")['logoUrl'],
|
|
703
742
|
doken: this.doken,
|
|
704
|
-
dokenRefreshCallback: async () =>
|
|
705
|
-
|
|
706
|
-
if (!this.doken)
|
|
707
|
-
throw new Error('[TIDECLOAK] No doken found');
|
|
708
|
-
return this.doken;
|
|
709
|
-
},
|
|
710
|
-
requireReloginCallback: async () => {
|
|
711
|
-
await this.login({
|
|
712
|
-
idpHint: 'tide',
|
|
713
|
-
prompt: 'login',
|
|
714
|
-
redirectUri: window.location.href
|
|
715
|
-
});
|
|
716
|
-
}
|
|
743
|
+
dokenRefreshCallback: async () => __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_provideDokenOrRelogin).call(this),
|
|
744
|
+
requireReloginCallback: async () => __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_reloginInteractively).call(this)
|
|
717
745
|
});
|
|
718
746
|
}
|
|
719
747
|
}
|
|
@@ -1444,6 +1472,15 @@ async function _TideCloak_processInit(initOptions) {
|
|
|
1444
1472
|
await this.login(options);
|
|
1445
1473
|
};
|
|
1446
1474
|
const onLoad = async () => {
|
|
1475
|
+
// Post-logout deterministic path: if the previous logout stamped a fresh
|
|
1476
|
+
// marker, skip silent check-sso entirely and go straight to interactive
|
|
1477
|
+
// login. There is no session to silently resume after an explicit logout,
|
|
1478
|
+
// and the silent path is exactly where the ERR_ABORTED wedge lives.
|
|
1479
|
+
if (__classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_consumePostLogoutMarker).call(this)) {
|
|
1480
|
+
__classPrivateFieldGet(this, _TideCloak_logInfo, "f").call(this, '[TIDE-POSTLOGOUT] marker honored in init(); forcing interactive login, bypassing silent check-sso');
|
|
1481
|
+
await doLogin(true);
|
|
1482
|
+
return;
|
|
1483
|
+
}
|
|
1447
1484
|
switch (initOptions.onLoad) {
|
|
1448
1485
|
case 'check-sso':
|
|
1449
1486
|
if (__classPrivateFieldGet(this, _TideCloak_loginIframe, "f").enable) {
|
|
@@ -1575,13 +1612,38 @@ async function _TideCloak_checkLoginIframe() {
|
|
|
1575
1612
|
*/
|
|
1576
1613
|
async function _TideCloak_checkSsoSilently() {
|
|
1577
1614
|
const iframe = document.createElement('iframe');
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1615
|
+
return await new Promise((resolve) => {
|
|
1616
|
+
let settled = false;
|
|
1617
|
+
/** @type {ReturnType<typeof setTimeout>=} */
|
|
1618
|
+
let timer;
|
|
1619
|
+
/** @type {ReturnType<typeof setTimeout>=} */
|
|
1620
|
+
let loadGraceTimer;
|
|
1621
|
+
const cleanup = () => {
|
|
1622
|
+
if (timer)
|
|
1623
|
+
clearTimeout(timer);
|
|
1624
|
+
if (loadGraceTimer)
|
|
1625
|
+
clearTimeout(loadGraceTimer);
|
|
1626
|
+
window.removeEventListener('message', messageCallback);
|
|
1627
|
+
iframe.removeEventListener('error', onIframeError);
|
|
1628
|
+
iframe.removeEventListener('load', onIframeLoad);
|
|
1629
|
+
if (iframe.parentNode)
|
|
1630
|
+
document.body.removeChild(iframe);
|
|
1631
|
+
};
|
|
1632
|
+
// Every NON-authenticating terminal outcome funnels through here exactly
|
|
1633
|
+
// once: onerror / onabort / load-without-message / timeout /
|
|
1634
|
+
// createLoginUrl-failure. Resolving (never rejecting) as NOT-authenticated
|
|
1635
|
+
// lets init() complete cleanly so the app falls through to interactive
|
|
1636
|
+
// login. This is what makes the silent path structurally un-hangable: no
|
|
1637
|
+
// matter which DOM event (or none) the aborted iframe produces, one of
|
|
1638
|
+
// these fires. `via` is logged so QA can prove which path was taken.
|
|
1639
|
+
const settleNotAuthenticated = (via) => {
|
|
1640
|
+
if (settled)
|
|
1641
|
+
return;
|
|
1642
|
+
settled = true;
|
|
1643
|
+
__classPrivateFieldGet(this, _TideCloak_logWarn, "f").call(this, '[TIDE-SILENTSSO] terminal=' + via + ' -> not-authenticated (authenticated=' + this.authenticated + ')');
|
|
1644
|
+
cleanup();
|
|
1645
|
+
resolve();
|
|
1646
|
+
};
|
|
1585
1647
|
/**
|
|
1586
1648
|
* @param {MessageEvent} event
|
|
1587
1649
|
*/
|
|
@@ -1589,18 +1651,63 @@ async function _TideCloak_checkSsoSilently() {
|
|
|
1589
1651
|
if (event.origin !== window.location.origin || iframe.contentWindow !== event.source) {
|
|
1590
1652
|
return;
|
|
1591
1653
|
}
|
|
1592
|
-
|
|
1654
|
+
if (settled)
|
|
1655
|
+
return;
|
|
1656
|
+
settled = true;
|
|
1657
|
+
cleanup();
|
|
1593
1658
|
try {
|
|
1659
|
+
const oauth = __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_parseCallback).call(this, event.data);
|
|
1594
1660
|
await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_processCallback).call(this, oauth);
|
|
1661
|
+
__classPrivateFieldGet(this, _TideCloak_logInfo, "f").call(this, '[TIDE-SILENTSSO] terminal=message -> processed (authenticated=' + this.authenticated + ')');
|
|
1595
1662
|
resolve();
|
|
1596
1663
|
}
|
|
1597
1664
|
catch (error) {
|
|
1598
|
-
reject
|
|
1665
|
+
// A failed silent callback must NOT reject: rejecting would surface as
|
|
1666
|
+
// an init() error / AuthWall wall instead of a clean fall-through to
|
|
1667
|
+
// interactive login. Resolve as not-authenticated.
|
|
1668
|
+
__classPrivateFieldGet(this, _TideCloak_logWarn, "f").call(this, '[TIDE-SILENTSSO] terminal=message-error -> not-authenticated: ' + (error instanceof Error ? error.message : String(error)));
|
|
1669
|
+
resolve();
|
|
1599
1670
|
}
|
|
1600
|
-
document.body.removeChild(iframe);
|
|
1601
|
-
window.removeEventListener('message', messageCallback);
|
|
1602
1671
|
};
|
|
1672
|
+
const onIframeError = () => settleNotAuthenticated('onerror');
|
|
1673
|
+
const onIframeLoad = () => {
|
|
1674
|
+
// The iframe finished navigating. On the happy path it landed on the
|
|
1675
|
+
// real silent-check-sso.html and `messageCallback` settles first. If it
|
|
1676
|
+
// loaded an error page, or a cross-origin KC page that never posts, or
|
|
1677
|
+
// an aborted navigation that still fired `load`, no message arrives - so
|
|
1678
|
+
// arm a short grace timer that settles NOT-authenticated well before the
|
|
1679
|
+
// outer ceiling.
|
|
1680
|
+
if (settled)
|
|
1681
|
+
return;
|
|
1682
|
+
const grace = Math.max(250, Math.min(2000, Math.floor(this.silentCheckSsoTimeout / 4)));
|
|
1683
|
+
if (loadGraceTimer)
|
|
1684
|
+
clearTimeout(loadGraceTimer);
|
|
1685
|
+
loadGraceTimer = setTimeout(() => settleNotAuthenticated('load-no-message'), grace);
|
|
1686
|
+
};
|
|
1687
|
+
// Wire ALL listeners before the navigation starts so no immediate event is
|
|
1688
|
+
// missed. `error` covers the browsers that surface a failed iframe
|
|
1689
|
+
// navigation as an error/abort event; `load` + the grace timer cover the
|
|
1690
|
+
// ones that fire load on an error document; the outer `timer` covers
|
|
1691
|
+
// net::ERR_ABORTED that produces NO DOM event at all.
|
|
1603
1692
|
window.addEventListener('message', messageCallback);
|
|
1693
|
+
iframe.addEventListener('error', onIframeError);
|
|
1694
|
+
iframe.addEventListener('load', onIframeLoad);
|
|
1695
|
+
// Hard ceiling. Created synchronously (NOT after an awaited createLoginUrl)
|
|
1696
|
+
// so it is always armed regardless of what the iframe navigation does.
|
|
1697
|
+
timer = setTimeout(() => settleNotAuthenticated('timeout'), this.silentCheckSsoTimeout);
|
|
1698
|
+
// Build the URL and start the navigation. Failure here must still settle
|
|
1699
|
+
// (not reject) so init() stays deterministic.
|
|
1700
|
+
this.createLoginUrl({ prompt: 'none', redirectUri: this.silentCheckSsoRedirectUri })
|
|
1701
|
+
.then((src) => {
|
|
1702
|
+
if (settled)
|
|
1703
|
+
return;
|
|
1704
|
+
iframe.setAttribute('src', src);
|
|
1705
|
+
iframe.setAttribute('sandbox', 'allow-storage-access-by-user-activation allow-scripts allow-same-origin');
|
|
1706
|
+
iframe.setAttribute('title', 'keycloak-silent-check-sso');
|
|
1707
|
+
iframe.style.display = 'none';
|
|
1708
|
+
document.body.appendChild(iframe);
|
|
1709
|
+
})
|
|
1710
|
+
.catch(() => settleNotAuthenticated('createLoginUrl-error'));
|
|
1604
1711
|
});
|
|
1605
1712
|
}, _TideCloak_parseCallback = function _TideCloak_parseCallback(url) {
|
|
1606
1713
|
const oauth = __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_parseCallbackUrl).call(this, url);
|
|
@@ -1735,6 +1842,25 @@ async function _TideCloak_checkSsoSilently() {
|
|
|
1735
1842
|
await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_scheduleCheckIframe).call(this);
|
|
1736
1843
|
}
|
|
1737
1844
|
}
|
|
1845
|
+
}, _TideCloak_consumePostLogoutMarker = function _TideCloak_consumePostLogoutMarker() {
|
|
1846
|
+
try {
|
|
1847
|
+
if (typeof window === 'undefined' || !window.localStorage)
|
|
1848
|
+
return false;
|
|
1849
|
+
const raw = window.localStorage.getItem(POST_LOGOUT_MARKER_KEY);
|
|
1850
|
+
if (!raw)
|
|
1851
|
+
return false;
|
|
1852
|
+
window.localStorage.removeItem(POST_LOGOUT_MARKER_KEY);
|
|
1853
|
+
const ts = Number(raw);
|
|
1854
|
+
const fresh = Number.isFinite(ts) && (Date.now() - ts) < POST_LOGOUT_MARKER_TTL_MS;
|
|
1855
|
+
if (!fresh) {
|
|
1856
|
+
__classPrivateFieldGet(this, _TideCloak_logWarn, "f").call(this, '[TIDE-POSTLOGOUT] marker present but stale (age > ' + POST_LOGOUT_MARKER_TTL_MS + 'ms); ignoring');
|
|
1857
|
+
}
|
|
1858
|
+
return fresh;
|
|
1859
|
+
}
|
|
1860
|
+
catch (e) {
|
|
1861
|
+
__classPrivateFieldGet(this, _TideCloak_logWarn, "f").call(this, '[TIDE-POSTLOGOUT] could not read marker: ' + (e instanceof Error ? e.message : String(e)));
|
|
1862
|
+
return false;
|
|
1863
|
+
}
|
|
1738
1864
|
}, _TideCloak_fetchAccessToken =
|
|
1739
1865
|
/**
|
|
1740
1866
|
* @typedef {Object} AccessTokenResponse The successful token response from the authorization server, based on the {@link https://datatracker.ietf.org/doc/html/rfc6749#section-5.1 OAuth 2.0 Authorization Framework specification}.
|
|
@@ -1842,9 +1968,42 @@ async function _TideCloak_fetchRefreshToken(url, refreshToken, clientId) {
|
|
|
1842
1968
|
throw error;
|
|
1843
1969
|
}
|
|
1844
1970
|
}, _TideCloak_ensureRequestEnclaveOpen = function _TideCloak_ensureRequestEnclaveOpen() {
|
|
1845
|
-
|
|
1971
|
+
// Never (re)open the enclave without a doken. Post-logout the doken is
|
|
1972
|
+
// flushed; reopening a dokenless hidden enclave wedges it with
|
|
1973
|
+
// "Expecting doken in request".
|
|
1974
|
+
if (!this.requestEnclave || !this.doken)
|
|
1846
1975
|
return;
|
|
1847
1976
|
this.requestEnclave.checkEnclaveOpen();
|
|
1977
|
+
}, _TideCloak_reloginInteractively =
|
|
1978
|
+
/**
|
|
1979
|
+
* Trigger a full interactive login redirect. Used as the recovery leg when a
|
|
1980
|
+
* silent flow cannot proceed (e.g. the enclave asks for a doken refresh but
|
|
1981
|
+
* the doken was flushed at logout). Navigates the whole page to Keycloak.
|
|
1982
|
+
* @returns {Promise<void>}
|
|
1983
|
+
*/
|
|
1984
|
+
async function _TideCloak_reloginInteractively() {
|
|
1985
|
+
await this.login({
|
|
1986
|
+
idpHint: 'tide',
|
|
1987
|
+
prompt: 'login',
|
|
1988
|
+
redirectUri: window.location.href
|
|
1989
|
+
});
|
|
1990
|
+
}, _TideCloak_provideDokenOrRelogin =
|
|
1991
|
+
/**
|
|
1992
|
+
* Provide the current doken to the enclave, or fall through to interactive
|
|
1993
|
+
* login when there is none (post-logout / flushed state). Throwing a bare
|
|
1994
|
+
* error here would leave the enclave (and whatever awaits its doken-refresh
|
|
1995
|
+
* completion) hanging; instead we redirect to a full interactive login, which
|
|
1996
|
+
* recovers cleanly.
|
|
1997
|
+
* @returns {Promise<string>}
|
|
1998
|
+
*/
|
|
1999
|
+
async function _TideCloak_provideDokenOrRelogin() {
|
|
2000
|
+
await this.ensureTokenReady();
|
|
2001
|
+
if (!this.doken) {
|
|
2002
|
+
__classPrivateFieldGet(this, _TideCloak_logWarn, "f").call(this, '[TIDECLOAK] Enclave requested a doken refresh but no doken is present (post-logout). Falling through to interactive login.');
|
|
2003
|
+
await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_reloginInteractively).call(this);
|
|
2004
|
+
throw new Error('[TIDECLOAK] No doken found - redirecting to interactive login');
|
|
2005
|
+
}
|
|
2006
|
+
return this.doken;
|
|
1848
2007
|
}, _TideCloak_getVoucherUrl = function _TideCloak_getVoucherUrl() {
|
|
1849
2008
|
if (!this.tokenParsed)
|
|
1850
2009
|
throw new Error('User authentication required to access voucher service');
|
|
@@ -1918,9 +2077,32 @@ async function _TideCloak_fetchRefreshToken(url, refreshToken, clientId) {
|
|
|
1918
2077
|
else {
|
|
1919
2078
|
delete this.doken;
|
|
1920
2079
|
delete this.dokenParsed;
|
|
1921
|
-
|
|
1922
|
-
|
|
2080
|
+
// No doken present (e.g. post-logout / flushed state). Do NOT push a
|
|
2081
|
+
// "doken refresh" into the enclave: refreshing with an undefined doken
|
|
2082
|
+
// wedges the hidden enclave ("Expecting doken in request",
|
|
2083
|
+
// TIDE-SWE-UNHANDLED) and blocks the silent re-auth that runs after
|
|
2084
|
+
// logout. Instead tear the stale enclave(s) down so (a) no dokenless
|
|
2085
|
+
// refresh is ever sent, (b) the persistent user-gesture listener can no
|
|
2086
|
+
// longer reopen a dokenless hidden enclave (#ensureRequestEnclaveOpen
|
|
2087
|
+
// no-ops once requestEnclave is cleared), and (c) the next authenticated
|
|
2088
|
+
// flow re-creates a fresh enclave from a valid doken via
|
|
2089
|
+
// initRequestEnclave(). The app is then free to fall through to an
|
|
2090
|
+
// interactive login, which recovers cleanly.
|
|
2091
|
+
__classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_teardownEnclaves).call(this);
|
|
2092
|
+
}
|
|
2093
|
+
}, _TideCloak_teardownEnclaves = function _TideCloak_teardownEnclaves() {
|
|
2094
|
+
for (const key of ['requestEnclave', 'approvalEnclave']) {
|
|
2095
|
+
const enclave = this[key];
|
|
2096
|
+
if (!enclave)
|
|
2097
|
+
continue;
|
|
2098
|
+
try {
|
|
2099
|
+
if (typeof enclave.close === 'function')
|
|
2100
|
+
enclave.close();
|
|
2101
|
+
}
|
|
2102
|
+
catch (error) {
|
|
2103
|
+
__classPrivateFieldGet(this, _TideCloak_logWarn, "f").call(this, '[TIDECLOAK] Failed to close ' + key + ': ' + (error instanceof Error ? error.message : error));
|
|
1923
2104
|
}
|
|
2105
|
+
this[key] = undefined;
|
|
1924
2106
|
}
|
|
1925
2107
|
}, _TideCloak_getRealmUrl = function _TideCloak_getRealmUrl() {
|
|
1926
2108
|
if (typeof this.authServerUrl === 'undefined') {
|