@tidecloak/js 0.14.41-staging → 0.14.42-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.
Files changed (111) hide show
  1. package/README.md +32 -47
  2. package/dist/cjs/lib/tidecloak-dpop.js +151 -42
  3. package/dist/cjs/lib/tidecloak-dpop.js.map +1 -1
  4. package/dist/cjs/lib/tidecloak.js +300 -378
  5. package/dist/cjs/lib/tidecloak.js.map +1 -1
  6. package/dist/cjs/src/AdminAPI.js +12 -19
  7. package/dist/cjs/src/AdminAPI.js.map +1 -1
  8. package/dist/cjs/src/IAMService.js +155 -2098
  9. package/dist/cjs/src/IAMService.js.map +1 -1
  10. package/dist/cjs/src/index.js +1 -7
  11. package/dist/cjs/src/index.js.map +1 -1
  12. package/dist/esm/lib/tidecloak-dpop.js +151 -42
  13. package/dist/esm/lib/tidecloak-dpop.js.map +1 -1
  14. package/dist/esm/lib/tidecloak.js +300 -377
  15. package/dist/esm/lib/tidecloak.js.map +1 -1
  16. package/dist/esm/src/AdminAPI.js +3 -7
  17. package/dist/esm/src/AdminAPI.js.map +1 -1
  18. package/dist/esm/src/IAMService.js +152 -2065
  19. package/dist/esm/src/IAMService.js.map +1 -1
  20. package/dist/esm/src/index.js +1 -7
  21. package/dist/esm/src/index.js.map +1 -1
  22. package/dist/types/AdminAPI.d.ts +1 -1
  23. package/dist/types/IAMService.d.ts +52 -603
  24. package/dist/types/index.d.ts +1 -11
  25. package/dist/types/lib/tidecloak-dpop.d.ts +19 -3
  26. package/dist/types/lib/tidecloak.d.ts +46 -38
  27. package/dist/types/src/AdminAPI.d.ts +1 -1
  28. package/dist/types/src/IAMService.d.ts +52 -603
  29. package/dist/types/src/index.d.ts +1 -11
  30. package/docs/FRONT_CHANNEL.md +55 -55
  31. package/lib/README.md +10 -14
  32. package/package.json +4 -25
  33. package/dist/cjs/lib/secureFetchPolicy.js +0 -45
  34. package/dist/cjs/lib/secureFetchPolicy.js.map +0 -1
  35. package/dist/cjs/src/dpopRoute.js +0 -30
  36. package/dist/cjs/src/dpopRoute.js.map +0 -1
  37. package/dist/cjs/src/dpopServer.js +0 -77
  38. package/dist/cjs/src/dpopServer.js.map +0 -1
  39. package/dist/cjs/src/generated/dpopAuthAsset.js +0 -7
  40. package/dist/cjs/src/generated/dpopAuthAsset.js.map +0 -1
  41. package/dist/cjs/src/policy-react.js +0 -51
  42. package/dist/cjs/src/policy-react.js.map +0 -1
  43. package/dist/cjs/src/types.js +0 -6
  44. package/dist/cjs/src/types.js.map +0 -1
  45. package/dist/cjs/src/utils/dpopAuthPage.js +0 -69
  46. package/dist/cjs/src/utils/dpopAuthPage.js.map +0 -1
  47. package/dist/cjs/src/utils/fetch.js +0 -39
  48. package/dist/cjs/src/utils/fetch.js.map +0 -1
  49. package/dist/cjs/src/utils/index.js +0 -17
  50. package/dist/cjs/src/utils/index.js.map +0 -1
  51. package/dist/cjs/src/utils/initOptions.js +0 -78
  52. package/dist/cjs/src/utils/initOptions.js.map +0 -1
  53. package/dist/cjs/src/utils/pkce.js +0 -48
  54. package/dist/cjs/src/utils/pkce.js.map +0 -1
  55. package/dist/cjs/src/utils/silentCheckSso.js +0 -138
  56. package/dist/cjs/src/utils/silentCheckSso.js.map +0 -1
  57. package/dist/cjs/src/vite.js +0 -43
  58. package/dist/cjs/src/vite.js.map +0 -1
  59. package/dist/esm/lib/secureFetchPolicy.js +0 -42
  60. package/dist/esm/lib/secureFetchPolicy.js.map +0 -1
  61. package/dist/esm/src/dpopRoute.js +0 -27
  62. package/dist/esm/src/dpopRoute.js.map +0 -1
  63. package/dist/esm/src/dpopServer.js +0 -68
  64. package/dist/esm/src/dpopServer.js.map +0 -1
  65. package/dist/esm/src/generated/dpopAuthAsset.js +0 -4
  66. package/dist/esm/src/generated/dpopAuthAsset.js.map +0 -1
  67. package/dist/esm/src/policy-react.js +0 -45
  68. package/dist/esm/src/policy-react.js.map +0 -1
  69. package/dist/esm/src/types.js +0 -5
  70. package/dist/esm/src/types.js.map +0 -1
  71. package/dist/esm/src/utils/dpopAuthPage.js +0 -63
  72. package/dist/esm/src/utils/dpopAuthPage.js.map +0 -1
  73. package/dist/esm/src/utils/fetch.js +0 -36
  74. package/dist/esm/src/utils/fetch.js.map +0 -1
  75. package/dist/esm/src/utils/index.js +0 -6
  76. package/dist/esm/src/utils/index.js.map +0 -1
  77. package/dist/esm/src/utils/initOptions.js +0 -75
  78. package/dist/esm/src/utils/initOptions.js.map +0 -1
  79. package/dist/esm/src/utils/pkce.js +0 -43
  80. package/dist/esm/src/utils/pkce.js.map +0 -1
  81. package/dist/esm/src/utils/silentCheckSso.js +0 -134
  82. package/dist/esm/src/utils/silentCheckSso.js.map +0 -1
  83. package/dist/esm/src/vite.js +0 -40
  84. package/dist/esm/src/vite.js.map +0 -1
  85. package/dist/types/dpopRoute.d.ts +0 -11
  86. package/dist/types/dpopServer.d.ts +0 -35
  87. package/dist/types/generated/dpopAuthAsset.d.ts +0 -2
  88. package/dist/types/lib/secureFetchPolicy.d.ts +0 -37
  89. package/dist/types/policy-react.d.ts +0 -40
  90. package/dist/types/src/dpopRoute.d.ts +0 -11
  91. package/dist/types/src/dpopServer.d.ts +0 -35
  92. package/dist/types/src/generated/dpopAuthAsset.d.ts +0 -2
  93. package/dist/types/src/policy-react.d.ts +0 -40
  94. package/dist/types/src/types.d.ts +0 -114
  95. package/dist/types/src/utils/dpopAuthPage.d.ts +0 -22
  96. package/dist/types/src/utils/fetch.d.ts +0 -11
  97. package/dist/types/src/utils/index.d.ts +0 -5
  98. package/dist/types/src/utils/initOptions.d.ts +0 -69
  99. package/dist/types/src/utils/pkce.d.ts +0 -24
  100. package/dist/types/src/utils/silentCheckSso.d.ts +0 -82
  101. package/dist/types/src/vite.d.ts +0 -13
  102. package/dist/types/types.d.ts +0 -114
  103. package/dist/types/utils/dpopAuthPage.d.ts +0 -22
  104. package/dist/types/utils/fetch.d.ts +0 -11
  105. package/dist/types/utils/index.d.ts +0 -5
  106. package/dist/types/utils/initOptions.d.ts +0 -69
  107. package/dist/types/utils/pkce.d.ts +0 -24
  108. package/dist/types/utils/silentCheckSso.d.ts +0 -82
  109. package/dist/types/vite.d.ts +0 -13
  110. package/docs/HYBRID_MODE.md +0 -185
  111. package/docs/NATIVE_MODE.md +0 -284
@@ -70,20 +70,12 @@ 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_popupErrorCallback, _TideCloak_logInfo, _TideCloak_logWarn, _TideCloak_loginIframe, _TideCloak_dpopProvider, _TideCloak_warnedStaleBearer, _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_handlePopupError, _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;
73
+ var _TideCloak_instances, _TideCloak_refreshQueue, _TideCloak_adapter, _TideCloak_useNonce, _TideCloak_callbackStorage, _TideCloak_popupErrorCallback, _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_check3pCookiesSupported, _TideCloak_processInit, _TideCloak_setupCheckLoginIframe, _TideCloak_checkLoginIframe, _TideCloak_checkSsoSilently, _TideCloak_parseCallback, _TideCloak_parseCallbackUrl, _TideCloak_parseCallbackParams, _TideCloak_processCallback, _TideCloak_scheduleCheckIframe, _TideCloak_dPoPFetch, _TideCloak_delegationFetch, _TideCloak_fetchExtensionChain, _TideCloak_fetchAccessToken, _TideCloak_validateDPoPTokenType, _TideCloak_fetchWithDPoPProof, _TideCloak_fetchWithDPoPDelegationProof, _TideCloak_fetchDPoPToken, _TideCloak_fetchRefreshToken, _TideCloak_ensureRequestEnclaveOpen, _TideCloak_reloginInteractively, _TideCloak_provideDokenOrRelogin, _TideCloak_handlePopupError, _TideCloak_getVoucherUrl, _TideCloak_setToken, _TideCloak_teardownEnclaves, _TideCloak_getRealmUrl, _TideCloak_getIssuerUrl, _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
- exports.NetworkError = exports.Models = exports.Tools = exports.PolicySignRequest = exports.ApprovalEnclaveNew = exports.ApprovalEnclave = exports.RequestEnclave = exports.shouldAttachDpopProof = void 0;
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).
82
- // `logout()` stamps this durable localStorage marker (it survives the logout
83
- // redirect round-trip); init() consumes it exactly once and, on the `check-sso`
84
- // path, settles as not-authenticated so the app shows its logged-out state.
85
- const POST_LOGOUT_MARKER_KEY = 'tide-post-logout';
86
- const POST_LOGOUT_MARKER_TTL_MS = 60000;
87
79
  /**
88
80
  * @typedef {Object} Endpoints
89
81
  * @property {() => string} authorize
@@ -102,9 +94,6 @@ const POST_LOGOUT_MARKER_TTL_MS = 60000;
102
94
  * @property {HTMLIFrameElement=} iframe
103
95
  * @property {string=} iframeOrigin
104
96
  */
105
- const secureFetchPolicy_js_1 = require("./secureFetchPolicy.js");
106
- var secureFetchPolicy_js_2 = require("./secureFetchPolicy.js");
107
- Object.defineProperty(exports, "shouldAttachDpopProof", { enumerable: true, get: function () { return secureFetchPolicy_js_2.shouldAttachDpopProof; } });
108
97
  var heimdall_tide_2 = require("heimdall-tide");
109
98
  Object.defineProperty(exports, "RequestEnclave", { enumerable: true, get: function () { return heimdall_tide_2.RequestEnclave; } });
110
99
  Object.defineProperty(exports, "ApprovalEnclave", { enumerable: true, get: function () { return heimdall_tide_2.ApprovalEnclave; } });
@@ -147,15 +136,6 @@ class TideCloak {
147
136
  );
148
137
  /** @type {import('./tidecloak-dpop.js').DPoPSignatureProvider=} */
149
138
  _TideCloak_dpopProvider.set(this, void 0);
150
- /**
151
- * The last non-matching `Authorization: Bearer …` value `secureFetch` warned
152
- * about, so a consumer stuck in a stale-token state gets ONE warning per stale
153
- * token rather than one per request. See `secureFetch`.
154
- * @type {string|null}
155
- */
156
- _TideCloak_warnedStaleBearer.set(this, null
157
- /** @type {TideCloakConfig} config */
158
- );
159
139
  /** @type {TideCloakConfig} config */
160
140
  _TideCloak_config.set(this, void 0);
161
141
  this.didInitialize = false;
@@ -171,15 +151,6 @@ class TideCloak {
171
151
  this.timeSkew = null;
172
152
  /** @type {boolean} */
173
153
  this.silentCheckSsoFallback = true;
174
- /**
175
- * Max time (ms) to wait for the hidden silent-check-sso iframe to post back
176
- * before treating the attempt as "not authenticated". Guards against the
177
- * post-logout wedge where the check-sso (or the Tide enclave it spins up)
178
- * never responds and init() would otherwise hang forever. Overridable via
179
- * initOptions.silentCheckSsoTimeout.
180
- * @type {number}
181
- */
182
- this.silentCheckSsoTimeout = 10000;
183
154
  /** @type {TideCloakPkceMethod} */
184
155
  this.pkceMethod = 'S256';
185
156
  this.enableLogging = false;
@@ -192,6 +163,7 @@ class TideCloak {
192
163
  */
193
164
  this.init = async (initOptions = {}) => {
194
165
  var _a, _b, _c;
166
+ var _d;
195
167
  if (this.didInitialize) {
196
168
  throw new Error("A 'TideCloak' instance can only be initialized once.");
197
169
  }
@@ -258,9 +230,6 @@ class TideCloak {
258
230
  if (typeof initOptions.silentCheckSsoFallback === 'boolean') {
259
231
  this.silentCheckSsoFallback = initOptions.silentCheckSsoFallback;
260
232
  }
261
- if (typeof initOptions.silentCheckSsoTimeout === 'number' && initOptions.silentCheckSsoTimeout > 0) {
262
- this.silentCheckSsoTimeout = initOptions.silentCheckSsoTimeout;
263
- }
264
233
  if (typeof initOptions.pkceMethod !== 'undefined') {
265
234
  if (initOptions.pkceMethod !== 'S256' && initOptions.pkceMethod !== false) {
266
235
  throw new TypeError(`Invalid value for pkceMethod', expected 'S256' or false but got ${initOptions.pkceMethod}.`);
@@ -279,15 +248,15 @@ class TideCloak {
279
248
  if (typeof initOptions.messageReceiveTimeout === 'number' && initOptions.messageReceiveTimeout > 0) {
280
249
  this.messageReceiveTimeout = initOptions.messageReceiveTimeout;
281
250
  }
282
- if (initOptions.useDPoP) {
283
- this.useDPoP = initOptions.useDPoP;
251
+ if (initOptions.dpopConfig) {
252
+ this.dpopConfig = initOptions.dpopConfig;
284
253
  }
285
254
  await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_loadConfig).call(this);
286
- if (((_a = this.useDPoP) === null || _a === void 0 ? void 0 : _a.mode) === 'strict' && (!this.dpopSigningAlgValuesSupported || this.dpopSigningAlgValuesSupported.length === 0)) {
255
+ if (((_a = this.dpopConfig) === null || _a === void 0 ? void 0 : _a.mode) === 'strict' && (!this.dpopSigningAlgValuesSupported || this.dpopSigningAlgValuesSupported.length === 0)) {
287
256
  throw new Error('DPoP is set to strict mode but the server does not advertise DPoP support (dpop_signing_alg_values_supported is missing or empty).');
288
257
  }
289
258
  // Initialize DPoP if enabled and server supports it
290
- if (this.useDPoP && ((_b = this.dpopSigningAlgValuesSupported) === null || _b === void 0 ? void 0 : _b.length)) {
259
+ if (this.dpopConfig && ((_b = this.dpopSigningAlgValuesSupported) === null || _b === void 0 ? void 0 : _b.length)) {
291
260
  const issuerUrl = __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_getIssuerUrl).call(this);
292
261
  if (!issuerUrl)
293
262
  throw new Error('Cannot initialize DPoP: issuer URL is not available. Ensure authServerUrl and realm are configured, or use OIDC provider mode with a valid issuer.');
@@ -298,7 +267,8 @@ class TideCloak {
298
267
  issuerUrl: new URL(issuerUrl),
299
268
  clientId: this.clientId,
300
269
  serverSupportedAlgorithms: this.dpopSigningAlgValuesSupported,
301
- requestedAlgorithm: !this.useDPoP.alg ? BrowserSignatureAlgs.ES256 : BrowserSignatureAlgs[this.useDPoP.alg],
270
+ requestedAlgorithm: !this.dpopConfig.alg ? BrowserSignatureAlgs.ES256 : BrowserSignatureAlgs[this.dpopConfig.alg],
271
+ strictStorage: (_d = this.dpopConfig.strictStorage) !== null && _d !== void 0 ? _d : false,
302
272
  getTimeSkew: () => { var _a; return (_a = this.timeSkew) !== null && _a !== void 0 ? _a : 0; }
303
273
  }), "f");
304
274
  await __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f").init();
@@ -307,30 +277,9 @@ class TideCloak {
307
277
  await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_check3pCookiesSupported).call(this);
308
278
  await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_processInit).call(this, initOptions);
309
279
  (_c = this.onReady) === null || _c === void 0 ? void 0 : _c.call(this, this.authenticated);
310
- // Do NOT eagerly open the Tide RequestEnclave during init().
311
- //
312
- // On a post-logout silent re-auth, the doken returned by the silent
313
- // check-sso can be STALE: its session key no longer matches the session the
314
- // hidden enclave cached from the previous login. Opening the hidden enclave
315
- // here makes it sit "waiting for doken refresh" forever - a refresh that
316
- // never comes because no fresh doken exists - which is exactly the
317
- // "[ENCLAVE] Received init but waiting for doken refresh" ... hang that
318
- // wedges the SPA on "Signing you in..." on the second login. (The enclave
319
- // silently WAITS rather than emitting an error, so heimdall's own
320
- // requireReloginCallback recovery never fires.)
321
- //
322
- // Instead we DEFER enclave setup: it is created lazily on the first real
323
- // Tide operation (encrypt / decrypt / approve / signDpopApproval - each
324
- // calls initRequestEnclave()), by which point a fresh interactive login has
325
- // minted a current doken and the enclave can complete its handshake. We
326
- // still register the user-gesture listener so the popup fallback can open
327
- // once an enclave has actually been created; #ensureRequestEnclaveOpen
328
- // no-ops while there is no enclave or no doken.
280
+ // The RequestEnclave is created lazily on the first Tide operation. Popups
281
+ // need a user gesture, so reopen it on click.
329
282
  if (initOptions.setupRequestEnclave) {
330
- if (this.doken) {
331
- __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).');
332
- }
333
- // to get around popups requiring user gestures
334
283
  document.addEventListener('click', () => {
335
284
  __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_ensureRequestEnclaveOpen).call(this);
336
285
  });
@@ -420,8 +369,7 @@ class TideCloak {
420
369
  }
421
370
  }
422
371
  if (__classPrivateFieldGet(this, _TideCloak_dpopProvider, "f")) {
423
- const thumbprint = await __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f").generateJWKThumbprint();
424
- params.append('dpop_jkt', thumbprint);
372
+ params.append('dpop_jkt', await __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f").generateJWKThumbprint());
425
373
  }
426
374
  __classPrivateFieldGet(this, _TideCloak_callbackStorage, "f").add(callbackState);
427
375
  return `${url}?${params.toString()}`;
@@ -433,17 +381,6 @@ class TideCloak {
433
381
  this.logout = async (options) => {
434
382
  var _a;
435
383
  await ((_a = __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f")) === null || _a === void 0 ? void 0 : _a.flush());
436
- // Stamp the post-logout marker so the next init() skips the racy silent
437
- // check-sso instead of trying to resume a session we just ended. See
438
- // POST_LOGOUT_MARKER_KEY.
439
- try {
440
- if (typeof window !== 'undefined' && window.localStorage) {
441
- window.localStorage.setItem(POST_LOGOUT_MARKER_KEY, Date.now().toString());
442
- }
443
- }
444
- catch (e) {
445
- __classPrivateFieldGet(this, _TideCloak_logWarn, "f").call(this, '[TIDE-POSTLOGOUT] could not set marker: ' + (e instanceof Error ? e.message : String(e)));
446
- }
447
384
  return __classPrivateFieldGet(this, _TideCloak_adapter, "f").logout(options);
448
385
  };
449
386
  /**
@@ -625,59 +562,89 @@ class TideCloak {
625
562
  }
626
563
  return await promise;
627
564
  };
628
- this.clearToken = () => {
629
- var _a;
565
+ this.clearToken = async () => {
566
+ var _a, _b, _c;
630
567
  if (this.token) {
568
+ await ((_a = __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f")) === null || _a === void 0 ? void 0 : _a.flush());
569
+ await ((_b = __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f")) === null || _b === void 0 ? void 0 : _b.init());
631
570
  __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_setToken).call(this);
632
- (_a = this.onAuthLogout) === null || _a === void 0 ? void 0 : _a.call(this);
571
+ (_c = this.onAuthLogout) === null || _c === void 0 ? void 0 : _c.call(this);
633
572
  if (this.loginRequired) {
634
- this.login();
573
+ await this.login();
635
574
  }
636
575
  }
637
576
  };
638
- this.secureFetch = async (url, init = {}) => {
639
- var _a, _b;
640
- const dpopProvider = __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f");
641
- if (dpopProvider && this.authenticated && this.token) {
642
- const existingAuth = new Headers(init.headers).get('Authorization');
643
- const isOurBearerToken = (0, secureFetchPolicy_js_1.shouldAttachDpopProof)(existingAuth, this.token);
644
- if (!isOurBearerToken) {
645
- // Quick escape - didn't put this check in first if statement as it's more expensive than other checks.
646
- //
647
- // BEHAVIOUR IS UNCHANGED: a request carrying some OTHER Bearer token is a
648
- // legitimate pass-through (a third-party API), so it goes out as a plain
649
- // fetch with no DPoP proof.
650
- //
651
- // But make it OBSERVABLE. When the caller hands us a Bearer token that is
652
- // almost-but-not-quite ours - i.e. a token this client issued them earlier
653
- // and that has since gone stale because they cached it and missed a refresh
654
- // - this silent downgrade is fatal and mute: a `dpop.bound.access.tokens`
655
- // realm rejects a DPoP-bound token presented as a plain Bearer with a bare
656
- // `401`, and nothing anywhere explains why. One warning per distinct stale
657
- // token (not per request), so a wedged consumer gets a clear signal instead
658
- // of a scrolling wall.
659
- if (typeof existingAuth === 'string' &&
660
- existingAuth.startsWith('Bearer ') &&
661
- existingAuth !== __classPrivateFieldGet(this, _TideCloak_warnedStaleBearer, "f")) {
662
- __classPrivateFieldSet(this, _TideCloak_warnedStaleBearer, existingAuth, "f");
663
- console.warn('[TIDECLOAK] secureFetch: the Authorization header carries a Bearer token that is NOT the ' +
664
- 'token this client currently holds. Sending it as a PLAIN fetch with no DPoP proof. If that ' +
665
- 'token came from this SDK it has gone STALE (the caller cached a copy and missed a refresh) ' +
666
- 'and a DPoP-bound realm will answer 401. Read the token from the SDK at call time ' +
667
- '(IAMService.getToken()) instead of holding a copy, or omit the Authorization header entirely ' +
668
- 'and let secureFetch attach it.');
577
+ this.fetch = async (url, init = {}) => {
578
+ let isRegularFetch = true;
579
+ let options = init; // object to hold all configurations made by extensions
580
+ for (let ext of __classPrivateFieldGet(this, _TideCloak_fetchExtensionChain, "f")) {
581
+ if (ext.use(url, options)) {
582
+ isRegularFetch = false;
583
+ const opts = await ext.configure(url, options);
584
+ if (opts)
585
+ options = opts; // only change if a change was made
586
+ }
587
+ else {
588
+ // this is really important
589
+ // fetchExtensionChain is a CHAIN
590
+ // a request might have dPoP use, then delegation use
591
+ // but it will NEVER have delegation use WIHTOUT dPoP use
592
+ // in that sense, to use tide delegation you need dpop + delegation being used first
593
+ break;
594
+ }
595
+ }
596
+ let response = await fetch(url, options);
597
+ if (isRegularFetch)
598
+ return response;
599
+ let safeguardCounter = __classPrivateFieldGet(this, _TideCloak_fetchExtensionChain, "f").length;
600
+ let handleableErrors = true;
601
+ while (safeguardCounter >= 0 && handleableErrors == true) {
602
+ handleableErrors = false;
603
+ for (let ext of __classPrivateFieldGet(this, _TideCloak_fetchExtensionChain, "f")) {
604
+ // we loop through the full chain since the developer might require tide delegation even though they didn't specify it
605
+ const updatedResponse = await ext.handleResponse(url, options, response);
606
+ if (updatedResponse) {
607
+ // if updated response then we want to continue the look to run all the extension handlers again
608
+ response = updatedResponse;
609
+ handleableErrors = true;
669
610
  }
670
- return fetch(url, init);
671
611
  }
672
- const urlString = url instanceof URL ? url.href : url.toString();
673
- const origin = new URL(urlString).origin;
612
+ safeguardCounter--; // in case the application somehow gets into a loop with dpop + delegation erros
613
+ }
614
+ return response;
615
+ };
616
+ _TideCloak_dPoPFetch.set(this, {
617
+ use: (url, init = {}) => {
618
+ const dpopProvider = __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f");
619
+ if (dpopProvider && this.authenticated && this.token) {
620
+ const existingAuth = new Headers(init.headers).get('Authorization');
621
+ const isOurBearerToken = existingAuth === `Bearer ${this.token}`;
622
+ if (isOurBearerToken)
623
+ return true;
624
+ }
625
+ return false;
626
+ },
627
+ configure: async (url, init = {}) => {
628
+ var _a;
629
+ const dpopProvider = __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f");
630
+ const requestUrl = new URL(String(url), document.baseURI);
631
+ const urlString = requestUrl.href;
632
+ const origin = requestUrl.origin;
674
633
  const method = (_a = init.method) !== null && _a !== void 0 ? _a : 'GET';
675
634
  const resourceNonce = dpopProvider.getResourceServerNonce(origin);
676
635
  const proof = await dpopProvider.generateDPoPProof(urlString, method, this.token, resourceNonce);
677
636
  const headers = new Headers(init.headers);
678
637
  headers.set('Authorization', `DPoP ${this.token}`);
679
638
  headers.set('DPoP', proof);
680
- const resp = await fetch(url, { ...init, headers });
639
+ return { ...init, headers };
640
+ },
641
+ handleResponse: async (url, init, resp) => {
642
+ var _a, _b;
643
+ const dpopProvider = __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f");
644
+ const requestUrl = new URL(String(url), document.baseURI);
645
+ const urlString = requestUrl.href;
646
+ const origin = requestUrl.origin;
647
+ const method = (_a = init.method) !== null && _a !== void 0 ? _a : 'GET';
681
648
  // Check for new nonce in response
682
649
  const newNonce = resp.headers.get('DPoP-Nonce');
683
650
  if (newNonce) {
@@ -688,11 +655,7 @@ class TideCloak {
688
655
  if (resp.status === 401 && newNonce) {
689
656
  const wwwAuth = (_b = resp.headers.get('WWW-Authenticate')) !== null && _b !== void 0 ? _b : '';
690
657
  if (wwwAuth.includes('DPoP') && wwwAuth.includes('error="use_dpop_nonce"')) {
691
- const retryProof = await dpopProvider.generateDPoPProof(urlString, method, this.token, newNonce);
692
- const retryHeaders = new Headers(init.headers);
693
- retryHeaders.set('Authorization', `DPoP ${this.token}`);
694
- retryHeaders.set('DPoP', retryProof);
695
- const retryResp = await fetch(url, { ...init, headers: retryHeaders });
658
+ const retryResp = await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_fetchWithDPoPProof).call(this, url, init, urlString, method, newNonce);
696
659
  // Capture nonce from retry response for future requests
697
660
  const retryNonce = retryResp.headers.get('DPoP-Nonce');
698
661
  if (retryNonce) {
@@ -701,38 +664,60 @@ class TideCloak {
701
664
  return retryResp;
702
665
  }
703
666
  }
704
- return resp;
667
+ return null; // indicate nothing was changed
705
668
  }
706
- else {
707
- // SAFETY INVARIANT: if a DPoP provider exists, this client's tokens are
708
- // sender-constrained (`cnf.jkt`), and a DPoP-bound token presented as a
709
- // plain `Bearer` is INVALID per RFC 9449 - the resource server answers a
710
- // bare `401` that names none of this. The silent fallback below is what
711
- // hid exactly that bug. So when we are about to drop out of the DPoP path
712
- // while STILL carrying an Authorization header, say so loudly - once per
713
- // distinct header value, so a wedged consumer gets a signal rather than a
714
- // scrolling wall.
715
- if (dpopProvider) {
716
- const existingAuth = new Headers(init.headers).get('Authorization');
717
- if (typeof existingAuth === 'string' &&
718
- existingAuth.startsWith('Bearer ') &&
719
- existingAuth !== __classPrivateFieldGet(this, _TideCloak_warnedStaleBearer, "f")) {
720
- __classPrivateFieldSet(this, _TideCloak_warnedStaleBearer, existingAuth, "f");
721
- console.error('[TIDECLOAK] secureFetch: DPoP is ENABLED on this client, but the request is going out as a ' +
722
- 'plain `Authorization: Bearer …` with NO DPoP proof' +
723
- (!this.authenticated
724
- ? ' (the client is not authenticated yet)'
725
- : !this.token
726
- ? ' (the client holds no access token yet)'
727
- : '') +
728
- '. If that token is DPoP-bound (`cnf.jkt`), the server will reject it with a bare 401. ' +
729
- 'Wait for the SDK to be authenticated and read the token from it at call time, or turn DPoP ' +
730
- 'off for this flow (omit `useDPoP` from the config).');
669
+ });
670
+ _TideCloak_delegationFetch.set(this, {
671
+ use: (url, init = {}) => {
672
+ return false;
673
+ },
674
+ configure: async (url, init = {}) => undefined, // cannot configure request withotu explicit server request
675
+ handleResponse: async (url, init, resp) => {
676
+ var _a, _b;
677
+ const dpopProvider = __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f");
678
+ const requestUrl = new URL(String(url), document.baseURI);
679
+ const urlString = requestUrl.href;
680
+ const method = (_a = init.method) !== null && _a !== void 0 ? _a : 'GET';
681
+ const delegationKey = resp.headers.get('Resource-Delegation-Key');
682
+ const delegationChallenge = resp.headers.get('Resource-Delegation-Challenge');
683
+ if (resp.status === 401 && delegationKey && delegationChallenge) {
684
+ const wwwAuth = (_b = resp.headers.get('WWW-Authenticate')) !== null && _b !== void 0 ? _b : '';
685
+ if (wwwAuth.includes('error="DelegationProofNotFound"')) {
686
+ const tokenId = this.tokenParsed.jti;
687
+ if (!tokenId) {
688
+ console.warn('[KEYCLOAK] Refusing DPoP delegation, access token does not include jti claim');
689
+ return;
690
+ }
691
+ const challengeMessage = `resource-token-id-challenge:${tokenId}`;
692
+ let dpopDelegationJwt;
693
+ try {
694
+ dpopDelegationJwt = await dpopProvider.generateResourceDelegation(delegationKey, challengeMessage, delegationChallenge, this.token);
695
+ }
696
+ catch (error) {
697
+ console.warn('[KEYCLOAK] Refusing DPoP delegation, returning original response:', error);
698
+ return;
699
+ }
700
+ if (resp.headers.get('Require-Tide-Delegation') === 'true') {
701
+ try {
702
+ this.initRequestEnclave();
703
+ }
704
+ catch (error) {
705
+ console.warn('[KEYCLOAK] Refusing Tide delegation, no request enclave available:', error);
706
+ }
707
+ // provide tide delegation
708
+ const delegation = bytesToBase64(await this.requestEnclave.requestExecutorWitnessSignature(StringToUint8Array(dpopDelegationJwt.substring(0, dpopDelegationJwt.lastIndexOf('.'))))); // sign only header and payload of delegation jwt, not signature (3rd .)
709
+ const headers = new Headers(init.headers);
710
+ headers.set('Enclave-Resource-Delegation-Signature', delegation);
711
+ init = { ...init, headers };
712
+ }
713
+ const retryResp = await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_fetchWithDPoPDelegationProof).call(this, url, init, urlString, method, dpopDelegationJwt);
714
+ return retryResp;
731
715
  }
732
716
  }
733
- return fetch(url, init);
717
+ return;
734
718
  }
735
- };
719
+ });
720
+ _TideCloak_fetchExtensionChain.set(this, [__classPrivateFieldGet(this, _TideCloak_dPoPFetch, "f"), __classPrivateFieldGet(this, _TideCloak_delegationFetch, "f")]);
736
721
  if (typeof config !== 'string' && !isObject(config)) {
737
722
  throw new Error("The 'TideCloak' constructor must be provided with a configuration object, or a URL to a JSON configuration file.");
738
723
  }
@@ -787,7 +772,7 @@ class TideCloak {
787
772
  signed_client_origin: __classPrivateFieldGet(this, _TideCloak_config, "f")['clientOriginAuth'],
788
773
  vendorId: __classPrivateFieldGet(this, _TideCloak_config, "f").vendorId,
789
774
  voucherURL: __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_getVoucherUrl).call(this),
790
- isRunningLocal: new URL(__classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_getVoucherUrl).call(this)).hostname === "localhost"
775
+ isRunningLocal: new URL(__classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_getVoucherUrl).call(this)).hostname === 'localhost'
791
776
  }).init({
792
777
  doken: this.doken,
793
778
  dokenRefreshCallback: async () => __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_provideDokenOrRelogin).call(this),
@@ -823,7 +808,7 @@ class TideCloak {
823
808
  /**
824
809
  * Role-based encryption via Tide RequestEnclave.
825
810
  * @param {{ data: string | Uint8Array, tags: string[] }[]} toEncrypt
826
- * @param {Uint8Array} decryption_policy If you'd like the data to be protected by a decryption policy
811
+ * @param {Uint8Array | null} [decryption_policy] If you'd like the data to be protected by a decryption policy
827
812
  * @returns {Promise<(string | Uint8Array)[]>}
828
813
  */
829
814
  async encrypt(toEncrypt, decryption_policy = null) {
@@ -837,7 +822,7 @@ class TideCloak {
837
822
  const dataToSend = toEncrypt.map((e) => {
838
823
  if (!isObject(e))
839
824
  throw new Error('All entries must be an object to encrypt');
840
- for (const property of ['data', 'tags']) {
825
+ for (const property of /** @type {const} */ (['data', 'tags'])) {
841
826
  if (!e[property]) {
842
827
  throw new Error(`The configuration object is missing the required '${property}' property.`);
843
828
  }
@@ -851,7 +836,7 @@ class TideCloak {
851
836
  if (typeof tag !== 'string')
852
837
  throw new Error('tags must be provided as an array of strings');
853
838
  if (!decryption_policy) {
854
- // if using standard encryption, check for doken includes default self encrypt roles
839
+ // Without a policy, the user needs the default self-encrypt role for the tag.
855
840
  const tagAccess = this.hasRealmRole(`_tide_${tag}.selfencrypt`);
856
841
  if (!tagAccess)
857
842
  throw new Error(`User has not been given any access to '${tag}'`);
@@ -860,17 +845,17 @@ class TideCloak {
860
845
  return {
861
846
  data: typeof e.data === 'string' ? StringToUint8Array(e.data) : e.data,
862
847
  tags: e.tags,
863
- isRaw: typeof e.data === 'string' ? false : true
848
+ isRaw: typeof e.data !== 'string'
864
849
  };
865
850
  });
866
851
  this.initRequestEnclave();
867
852
  const encrypted = await this.requestEnclave.encrypt(dataToSend, decryption_policy);
868
- return encrypted.map((cipher, i) => (dataToSend[i].isRaw ? cipher : bytesToBase64(cipher)));
853
+ return encrypted.map((/** @type {Uint8Array} */ cipher, /** @type {number} */ i) => (dataToSend[i].isRaw ? cipher : bytesToBase64(cipher)));
869
854
  }
870
855
  /**
871
- * Begin the process of drafting a encryption request
872
- * @param {{ data: string | Uint8Array, tags: string[] }[]} toEncrypt
873
- * @returns
856
+ * Begin drafting an encryption request.
857
+ * @param {{ data: Uint8Array, tags: string[] }[]} toEncrypt
858
+ * @returns {Promise<Uint8Array>}
874
859
  */
875
860
  async draftEncryption(toEncrypt) {
876
861
  await this.ensureTokenReady();
@@ -883,7 +868,7 @@ class TideCloak {
883
868
  const dataToSend = toEncrypt.map((e) => {
884
869
  if (!isObject(e))
885
870
  throw new Error('All entries must be an object to encrypt');
886
- for (const property of ['data', 'tags']) {
871
+ for (const property of /** @type {const} */ (['data', 'tags'])) {
887
872
  if (!e[property]) {
888
873
  throw new Error(`The configuration object is missing the required '${property}' property.`);
889
874
  }
@@ -899,28 +884,27 @@ class TideCloak {
899
884
  }
900
885
  return {
901
886
  data: e.data,
902
- tags: e.tags,
887
+ tags: e.tags
903
888
  };
904
889
  });
905
890
  this.initRequestEnclave();
906
891
  return await this.requestEnclave.draftEncryption(dataToSend);
907
892
  }
908
893
  /**
909
- * Commit a encryption request with a specified policy
894
+ * Commit an encryption request with a specified policy.
910
895
  * @param {Uint8Array} request
911
896
  * @param {Uint8Array} decryption_policy
912
897
  * @returns {Promise<Uint8Array[]>}
913
898
  */
914
899
  async commitEncryption(request, decryption_policy) {
915
- // Commit the encryption request and return data similar to encrypt()
916
900
  await this.ensureTokenReady();
917
901
  this.initRequestEnclave();
918
902
  return await this.requestEnclave.commitEncryption(request, decryption_policy);
919
903
  }
920
904
  /**
921
- * Begin the process of drafting a encryption request
922
- * @param {{ encrypted: string | Uint8Array, tags: string[] }[]} toDecrypt
923
- * @returns {Uint8Array}
905
+ * Begin drafting a decryption request.
906
+ * @param {{ encrypted: Uint8Array, tags: string[] }[]} toDecrypt
907
+ * @returns {Promise<Uint8Array>}
924
908
  */
925
909
  async draftDecryption(toDecrypt) {
926
910
  await this.ensureTokenReady();
@@ -933,7 +917,7 @@ class TideCloak {
933
917
  const dataToSend = toDecrypt.map((e) => {
934
918
  if (!isObject(e))
935
919
  throw new Error('All entries must be an object to decrypt');
936
- for (const property of ['encrypted', 'tags']) {
920
+ for (const property of /** @type {const} */ (['encrypted', 'tags'])) {
937
921
  if (!e[property]) {
938
922
  throw new Error(`The configuration object is missing the required '${property}' property.`);
939
923
  }
@@ -953,17 +937,15 @@ class TideCloak {
953
937
  };
954
938
  });
955
939
  this.initRequestEnclave();
956
- const decryptionRequest = await this.requestEnclave.draftDecryption(dataToSend);
957
- return decryptionRequest;
940
+ return await this.requestEnclave.draftDecryption(dataToSend);
958
941
  }
959
942
  /**
960
- * Commit a decryption request with a specified policy
943
+ * Commit a decryption request with a specified policy.
961
944
  * @param {Uint8Array} request
962
945
  * @param {Uint8Array} decryption_policy
963
946
  * @returns {Promise<Uint8Array[]>}
964
947
  */
965
948
  async commitDecryption(request, decryption_policy) {
966
- // Commit the encryption request and return data similar to encrypt()
967
949
  await this.ensureTokenReady();
968
950
  this.initRequestEnclave();
969
951
  return await this.requestEnclave.commitDecryption(request, decryption_policy);
@@ -989,10 +971,10 @@ class TideCloak {
989
971
  return await this.approvalEnclave.approve(requests);
990
972
  }
991
973
  /**
992
- * Execute a Tide Sign Request
974
+ * Execute a Tide Sign Request.
993
975
  * @param {Uint8Array} request
994
976
  * @param {boolean} [waitForAll=false]
995
- * @returns {Promise<Array>} Array of signatures
977
+ * @returns {Promise<any[]>} Array of signatures
996
978
  */
997
979
  async executeSignRequest(request, waitForAll = false) {
998
980
  await this.ensureTokenReady();
@@ -1002,7 +984,7 @@ class TideCloak {
1002
984
  /**
1003
985
  * Role-based decryption via Tide RequestEnclave.
1004
986
  * @param {{ encrypted: string | Uint8Array, tags: string[] }[]} toDecrypt
1005
- * @param {Uint8Array} decryption_policy If the data is protected by a decryption policy
987
+ * @param {Uint8Array | null} [decryption_policy] If the data is protected by a decryption policy
1006
988
  * @returns {Promise<(string | Uint8Array)[]>}
1007
989
  */
1008
990
  async decrypt(toDecrypt, decryption_policy = null) {
@@ -1016,7 +998,7 @@ class TideCloak {
1016
998
  const dataToSend = toDecrypt.map((e) => {
1017
999
  if (!isObject(e))
1018
1000
  throw new Error('All entries must be an object to decrypt');
1019
- for (const property of ['encrypted', 'tags']) {
1001
+ for (const property of /** @type {const} */ (['encrypted', 'tags'])) {
1020
1002
  if (!e[property]) {
1021
1003
  throw new Error(`The configuration object is missing the required '${property}' property.`);
1022
1004
  }
@@ -1030,7 +1012,7 @@ class TideCloak {
1030
1012
  if (typeof tag !== 'string')
1031
1013
  throw new Error('tags must be provided as an array of strings');
1032
1014
  if (!decryption_policy) {
1033
- // if using standard decryption, check for doken includes default self decrypt roles
1015
+ // Without a policy, the user needs the default self-decrypt role for the tag.
1034
1016
  const tagAccess = this.hasRealmRole(`_tide_${tag}.selfdecrypt`);
1035
1017
  if (!tagAccess)
1036
1018
  throw new Error(`User has not been given any access to '${tag}'`);
@@ -1039,15 +1021,15 @@ class TideCloak {
1039
1021
  return {
1040
1022
  encrypted: typeof e.encrypted === 'string' ? base64ToBytes(e.encrypted) : e.encrypted,
1041
1023
  tags: e.tags,
1042
- isRaw: typeof e.encrypted === 'string' ? false : true
1024
+ isRaw: typeof e.encrypted !== 'string'
1043
1025
  };
1044
1026
  });
1045
1027
  this.initRequestEnclave();
1046
1028
  const decrypted = await this.requestEnclave.decrypt(dataToSend, decryption_policy);
1047
- return decrypted.map((d, i) => (dataToSend[i].isRaw ? d : StringFromUint8Array(d)));
1029
+ return decrypted.map((/** @type {Uint8Array} */ d, /** @type {number} */ i) => (dataToSend[i].isRaw ? d : StringFromUint8Array(d)));
1048
1030
  }
1049
1031
  }
1050
- _TideCloak_refreshQueue = new WeakMap(), _TideCloak_adapter = new WeakMap(), _TideCloak_useNonce = new WeakMap(), _TideCloak_callbackStorage = new WeakMap(), _TideCloak_popupErrorCallback = new WeakMap(), _TideCloak_logInfo = new WeakMap(), _TideCloak_logWarn = new WeakMap(), _TideCloak_loginIframe = new WeakMap(), _TideCloak_dpopProvider = new WeakMap(), _TideCloak_warnedStaleBearer = new WeakMap(), _TideCloak_config = new WeakMap(), _TideCloak_instances = new WeakSet(), _TideCloak_loadAdapter = function _TideCloak_loadAdapter(type) {
1032
+ _TideCloak_refreshQueue = new WeakMap(), _TideCloak_adapter = new WeakMap(), _TideCloak_useNonce = new WeakMap(), _TideCloak_callbackStorage = new WeakMap(), _TideCloak_popupErrorCallback = new WeakMap(), _TideCloak_logInfo = new WeakMap(), _TideCloak_logWarn = new WeakMap(), _TideCloak_loginIframe = new WeakMap(), _TideCloak_dpopProvider = new WeakMap(), _TideCloak_config = new WeakMap(), _TideCloak_dPoPFetch = new WeakMap(), _TideCloak_delegationFetch = new WeakMap(), _TideCloak_fetchExtensionChain = new WeakMap(), _TideCloak_instances = new WeakSet(), _TideCloak_loadAdapter = function _TideCloak_loadAdapter(type) {
1051
1033
  if (type === 'default') {
1052
1034
  return __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_loadDefaultAdapter).call(this);
1053
1035
  }
@@ -1384,7 +1366,7 @@ async function _TideCloak_loadConfig() {
1384
1366
  */
1385
1367
  async function _TideCloak_setupEndpoints() {
1386
1368
  // Fetch OIDC metadata to get DPoP supported algorithms (only if DPoP is enabled)
1387
- if (this.useDPoP) {
1369
+ if (this.dpopConfig) {
1388
1370
  const issuerUrl = __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_getIssuerUrl).call(this);
1389
1371
  if (!issuerUrl) {
1390
1372
  __classPrivateFieldGet(this, _TideCloak_logWarn, "f").call(this, '[TIDECLOAK] Cannot fetch OIDC metadata: issuer URL is not available');
@@ -1470,11 +1452,6 @@ async function _TideCloak_loadOidcConfig(oidcProvider) {
1470
1452
  return config.userinfo_endpoint;
1471
1453
  }
1472
1454
  };
1473
- }, _TideCloak_getIssuerUrl = function _TideCloak_getIssuerUrl() {
1474
- if (this.issuer)
1475
- return this.issuer;
1476
- const realmUrl = __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_getRealmUrl).call(this);
1477
- return realmUrl || undefined;
1478
1455
  }, _TideCloak_check3pCookiesSupported =
1479
1456
  /**
1480
1457
  * @returns {Promise<void>}
@@ -1547,20 +1524,8 @@ async function _TideCloak_processInit(initOptions) {
1547
1524
  await this.login(options);
1548
1525
  };
1549
1526
  const onLoad = async () => {
1550
- // A fresh marker means we just came back from an explicit logout. The
1551
- // marker is here to skip the silent check-sso, which is racy (its hidden
1552
- // iframe can net::ERR_ABORTED and wedge the app) and pointless: there is
1553
- // no session left to resume. So `check-sso` settles as not-authenticated
1554
- // and the app renders its logged-out state. `login-required` still means
1555
- // what it says, so that path is unchanged. Consumed here either way, so
1556
- // the marker stays one-shot.
1557
- const afterLogout = __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_consumePostLogoutMarker).call(this);
1558
1527
  switch (initOptions.onLoad) {
1559
1528
  case 'check-sso':
1560
- if (afterLogout) {
1561
- __classPrivateFieldGet(this, _TideCloak_logInfo, "f").call(this, '[TIDE-POSTLOGOUT] marker honored in init(); settling as not-authenticated, bypassing silent check-sso');
1562
- return;
1563
- }
1564
1529
  if (__classPrivateFieldGet(this, _TideCloak_loginIframe, "f").enable) {
1565
1530
  await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_setupCheckLoginIframe).call(this);
1566
1531
  const unchanged = await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_checkLoginIframe).call(this);
@@ -1690,38 +1655,13 @@ async function _TideCloak_checkLoginIframe() {
1690
1655
  */
1691
1656
  async function _TideCloak_checkSsoSilently() {
1692
1657
  const iframe = document.createElement('iframe');
1693
- return await new Promise((resolve) => {
1694
- let settled = false;
1695
- /** @type {ReturnType<typeof setTimeout>=} */
1696
- let timer;
1697
- /** @type {ReturnType<typeof setTimeout>=} */
1698
- let loadGraceTimer;
1699
- const cleanup = () => {
1700
- if (timer)
1701
- clearTimeout(timer);
1702
- if (loadGraceTimer)
1703
- clearTimeout(loadGraceTimer);
1704
- window.removeEventListener('message', messageCallback);
1705
- iframe.removeEventListener('error', onIframeError);
1706
- iframe.removeEventListener('load', onIframeLoad);
1707
- if (iframe.parentNode)
1708
- document.body.removeChild(iframe);
1709
- };
1710
- // Every NON-authenticating terminal outcome funnels through here exactly
1711
- // once: onerror / onabort / load-without-message / timeout /
1712
- // createLoginUrl-failure. Resolving (never rejecting) as NOT-authenticated
1713
- // lets init() complete cleanly so the app falls through to interactive
1714
- // login. This is what makes the silent path structurally un-hangable: no
1715
- // matter which DOM event (or none) the aborted iframe produces, one of
1716
- // these fires. `via` is logged so QA can prove which path was taken.
1717
- const settleNotAuthenticated = (via) => {
1718
- if (settled)
1719
- return;
1720
- settled = true;
1721
- __classPrivateFieldGet(this, _TideCloak_logWarn, "f").call(this, '[TIDE-SILENTSSO] terminal=' + via + ' -> not-authenticated (authenticated=' + this.authenticated + ')');
1722
- cleanup();
1723
- resolve();
1724
- };
1658
+ const src = await this.createLoginUrl({ prompt: 'none', redirectUri: this.silentCheckSsoRedirectUri });
1659
+ iframe.setAttribute('src', src);
1660
+ iframe.setAttribute('sandbox', 'allow-storage-access-by-user-activation allow-scripts allow-same-origin');
1661
+ iframe.setAttribute('title', 'keycloak-silent-check-sso');
1662
+ iframe.style.display = 'none';
1663
+ document.body.appendChild(iframe);
1664
+ return await new Promise((resolve, reject) => {
1725
1665
  /**
1726
1666
  * @param {MessageEvent} event
1727
1667
  */
@@ -1729,63 +1669,18 @@ async function _TideCloak_checkSsoSilently() {
1729
1669
  if (event.origin !== window.location.origin || iframe.contentWindow !== event.source) {
1730
1670
  return;
1731
1671
  }
1732
- if (settled)
1733
- return;
1734
- settled = true;
1735
- cleanup();
1672
+ const oauth = __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_parseCallback).call(this, event.data);
1736
1673
  try {
1737
- const oauth = __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_parseCallback).call(this, event.data);
1738
1674
  await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_processCallback).call(this, oauth);
1739
- __classPrivateFieldGet(this, _TideCloak_logInfo, "f").call(this, '[TIDE-SILENTSSO] terminal=message -> processed (authenticated=' + this.authenticated + ')');
1740
1675
  resolve();
1741
1676
  }
1742
1677
  catch (error) {
1743
- // A failed silent callback must NOT reject: rejecting would surface as
1744
- // an init() error / AuthWall wall instead of a clean fall-through to
1745
- // interactive login. Resolve as not-authenticated.
1746
- __classPrivateFieldGet(this, _TideCloak_logWarn, "f").call(this, '[TIDE-SILENTSSO] terminal=message-error -> not-authenticated: ' + (error instanceof Error ? error.message : String(error)));
1747
- resolve();
1678
+ reject(error);
1748
1679
  }
1680
+ document.body.removeChild(iframe);
1681
+ window.removeEventListener('message', messageCallback);
1749
1682
  };
1750
- const onIframeError = () => settleNotAuthenticated('onerror');
1751
- const onIframeLoad = () => {
1752
- // The iframe finished navigating. On the happy path it landed on the
1753
- // real silent-check-sso.html and `messageCallback` settles first. If it
1754
- // loaded an error page, or a cross-origin KC page that never posts, or
1755
- // an aborted navigation that still fired `load`, no message arrives - so
1756
- // arm a short grace timer that settles NOT-authenticated well before the
1757
- // outer ceiling.
1758
- if (settled)
1759
- return;
1760
- const grace = Math.max(250, Math.min(2000, Math.floor(this.silentCheckSsoTimeout / 4)));
1761
- if (loadGraceTimer)
1762
- clearTimeout(loadGraceTimer);
1763
- loadGraceTimer = setTimeout(() => settleNotAuthenticated('load-no-message'), grace);
1764
- };
1765
- // Wire ALL listeners before the navigation starts so no immediate event is
1766
- // missed. `error` covers the browsers that surface a failed iframe
1767
- // navigation as an error/abort event; `load` + the grace timer cover the
1768
- // ones that fire load on an error document; the outer `timer` covers
1769
- // net::ERR_ABORTED that produces NO DOM event at all.
1770
1683
  window.addEventListener('message', messageCallback);
1771
- iframe.addEventListener('error', onIframeError);
1772
- iframe.addEventListener('load', onIframeLoad);
1773
- // Hard ceiling. Created synchronously (NOT after an awaited createLoginUrl)
1774
- // so it is always armed regardless of what the iframe navigation does.
1775
- timer = setTimeout(() => settleNotAuthenticated('timeout'), this.silentCheckSsoTimeout);
1776
- // Build the URL and start the navigation. Failure here must still settle
1777
- // (not reject) so init() stays deterministic.
1778
- this.createLoginUrl({ prompt: 'none', redirectUri: this.silentCheckSsoRedirectUri })
1779
- .then((src) => {
1780
- if (settled)
1781
- return;
1782
- iframe.setAttribute('src', src);
1783
- iframe.setAttribute('sandbox', 'allow-storage-access-by-user-activation allow-scripts allow-same-origin');
1784
- iframe.setAttribute('title', 'keycloak-silent-check-sso');
1785
- iframe.style.display = 'none';
1786
- document.body.appendChild(iframe);
1787
- })
1788
- .catch(() => settleNotAuthenticated('createLoginUrl-error'));
1789
1684
  });
1790
1685
  }, _TideCloak_parseCallback = function _TideCloak_parseCallback(url) {
1791
1686
  const oauth = __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_parseCallbackUrl).call(this, url);
@@ -1920,36 +1815,17 @@ async function _TideCloak_checkSsoSilently() {
1920
1815
  await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_scheduleCheckIframe).call(this);
1921
1816
  }
1922
1817
  }
1923
- }, _TideCloak_consumePostLogoutMarker = function _TideCloak_consumePostLogoutMarker() {
1924
- try {
1925
- if (typeof window === 'undefined' || !window.localStorage)
1926
- return false;
1927
- const raw = window.localStorage.getItem(POST_LOGOUT_MARKER_KEY);
1928
- if (!raw)
1929
- return false;
1930
- window.localStorage.removeItem(POST_LOGOUT_MARKER_KEY);
1931
- const ts = Number(raw);
1932
- const fresh = Number.isFinite(ts) && (Date.now() - ts) < POST_LOGOUT_MARKER_TTL_MS;
1933
- if (!fresh) {
1934
- __classPrivateFieldGet(this, _TideCloak_logWarn, "f").call(this, '[TIDE-POSTLOGOUT] marker present but stale (age > ' + POST_LOGOUT_MARKER_TTL_MS + 'ms); ignoring');
1935
- }
1936
- return fresh;
1937
- }
1938
- catch (e) {
1939
- __classPrivateFieldGet(this, _TideCloak_logWarn, "f").call(this, '[TIDE-POSTLOGOUT] could not read marker: ' + (e instanceof Error ? e.message : String(e)));
1940
- return false;
1941
- }
1942
1818
  }, _TideCloak_fetchAccessToken =
1943
1819
  /**
1944
- * @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}.
1945
- * @property {string} access_token The access token issued by the authorization server.
1946
- * @property {string} token_type The type of the token issued by the authorization server.
1947
- * @property {number} [expires_in] The lifetime in seconds of the access token.
1948
- * @property {string} [refresh_token] The refresh token issued by the authorization server.
1949
- * @property {string} [id_token] The ID token issued by the authorization server, if requested.
1950
- * @property {string} [scope] The scope of the access token.
1951
- * @property {string} [doken] The Tide doken issued by the authorization server.
1952
- */
1820
+ * @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}.
1821
+ * @property {string} access_token The access token issued by the authorization server.
1822
+ * @property {string} token_type The type of the token issued by the authorization server.
1823
+ * @property {number} [expires_in] The lifetime in seconds of the access token.
1824
+ * @property {string} [refresh_token] The refresh token issued by the authorization server.
1825
+ * @property {string} [id_token] The ID token issued by the authorization server, if requested.
1826
+ * @property {string} [scope] The scope of the access token.
1827
+ * @property {string} [doken] The Tide doken issued by the authorization server.
1828
+ */
1953
1829
  /**
1954
1830
  * Fetch the access token from the given URL.
1955
1831
  * @param {string} url
@@ -1969,36 +1845,117 @@ async function _TideCloak_fetchAccessToken(url, code, clientId, redirectUri, pkc
1969
1845
  if (pkceCodeVerifier) {
1970
1846
  body.append('code_verifier', pkceCodeVerifier);
1971
1847
  }
1972
- /** @type {Record<string, string>} */
1973
- const headers = {};
1974
1848
  if (__classPrivateFieldGet(this, _TideCloak_dpopProvider, "f")) {
1975
- const nonce = await __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f").getAuthServerNonce();
1976
- headers['DPoP'] = await __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f").generateDPoPProof(url, 'POST', undefined, nonce);
1849
+ return await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_fetchDPoPToken).call(this, url, body);
1977
1850
  }
1978
- try {
1851
+ else {
1979
1852
  return await fetchJSON(url, {
1980
1853
  method: 'POST',
1981
1854
  credentials: 'include',
1982
- headers,
1983
1855
  body
1984
1856
  });
1985
1857
  }
1986
- catch (error) {
1987
- // Handle DPoP nonce retry for token endpoint
1988
- if (__classPrivateFieldGet(this, _TideCloak_dpopProvider, "f") && error instanceof NetworkError) {
1989
- const newNonce = error.response.headers.get('DPoP-Nonce');
1990
- if (newNonce) {
1991
- await __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f").updateAuthServerNonce(newNonce);
1992
- headers['DPoP'] = await __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f").generateDPoPProof(url, 'POST', undefined, newNonce);
1993
- return await fetchJSON(url, {
1994
- method: 'POST',
1995
- credentials: 'include',
1996
- headers,
1997
- body
1998
- });
1858
+ }, _TideCloak_validateDPoPTokenType = function _TideCloak_validateDPoPTokenType(tokenResponse) {
1859
+ var _a, _b;
1860
+ const tokenType = (_a = tokenResponse.token_type) === null || _a === void 0 ? void 0 : _a.toLowerCase();
1861
+ const hasDPoPProvider = !!__classPrivateFieldGet(this, _TideCloak_dpopProvider, "f");
1862
+ if (hasDPoPProvider && tokenType !== 'dpop') {
1863
+ // We sent a DPoP proof but server returned non-DPoP token
1864
+ if (((_b = this.dpopConfig) === null || _b === void 0 ? void 0 : _b.mode) === 'strict') {
1865
+ throw new Error('DPoP strict mode enabled but server returned non-DPoP token (token_type: ' + tokenResponse.token_type + ')');
1866
+ }
1867
+ // Auto mode: disable DPoP and fall back to Bearer
1868
+ __classPrivateFieldGet(this, _TideCloak_logWarn, "f").call(this, '[TIDECLOAK] Server returned token_type "' + tokenResponse.token_type + '" instead of "DPoP". Disabling DPoP for this session.');
1869
+ __classPrivateFieldSet(this, _TideCloak_dpopProvider, undefined, "f");
1870
+ }
1871
+ return tokenResponse;
1872
+ }, _TideCloak_fetchWithDPoPProof =
1873
+ /**
1874
+ * @param {RequestInfo | URL} url
1875
+ * @param {RequestInit} init
1876
+ * @param {string} urlString
1877
+ * @param {string} method
1878
+ * @param {string} [nonce]
1879
+ * @returns {Promise<Response>}
1880
+ */
1881
+ async function _TideCloak_fetchWithDPoPProof(url, init, urlString, method, nonce) {
1882
+ const dpopProvider = __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f");
1883
+ if (!dpopProvider) {
1884
+ throw new Error('DPoP provider not initialized');
1885
+ }
1886
+ const proof = await dpopProvider.generateDPoPProof(urlString, method, this.token, nonce);
1887
+ const headers = new Headers(init.headers);
1888
+ headers.set('Authorization', `DPoP ${this.token}`);
1889
+ headers.set('DPoP', proof);
1890
+ return fetch(url, { ...init, headers });
1891
+ }, _TideCloak_fetchWithDPoPDelegationProof =
1892
+ /**
1893
+ * @param {RequestInfo | URL} url
1894
+ * @param {RequestInit} init
1895
+ * @param {string} urlString
1896
+ * @param {string} method
1897
+ * @param {string} delegationJwt
1898
+ * @returns {Promise<Response>}
1899
+ */
1900
+ async function _TideCloak_fetchWithDPoPDelegationProof(url, init, urlString, method, delegationJwt) {
1901
+ const dpopProvider = __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f");
1902
+ if (!dpopProvider) {
1903
+ throw new Error('DPoP provider not initialized');
1904
+ }
1905
+ const origin = new URL(urlString).origin;
1906
+ const resourceNonce = dpopProvider.getResourceServerNonce(origin);
1907
+ const headers = new Headers(init.headers);
1908
+ headers.set('Resource-Delegation', delegationJwt);
1909
+ return __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_fetchWithDPoPProof).call(this, url, { ...init, headers }, urlString, method, resourceNonce);
1910
+ }, _TideCloak_fetchDPoPToken =
1911
+ /**
1912
+ * Fetch tokens from the authorization server with DPoP proof attached.
1913
+ * Handles nonce requirements per RFC 9449 Section 8.
1914
+ * @param {string} url
1915
+ * @param {URLSearchParams} body
1916
+ * @returns {Promise<AccessTokenResponse>}
1917
+ */
1918
+ async function _TideCloak_fetchDPoPToken(url, body) {
1919
+ const dpopProvider = __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f");
1920
+ if (!dpopProvider) {
1921
+ throw new Error('DPoP provider not initialized');
1922
+ }
1923
+ const fetchToken = async (nonce) => {
1924
+ const proof = await dpopProvider.generateDPoPProof(url, 'POST', undefined, nonce);
1925
+ const headers = new Headers();
1926
+ headers.set('DPoP', proof);
1927
+ headers.set('Accept', CONTENT_TYPE_JSON);
1928
+ const resp = await fetchWithErrorHandling(url, {
1929
+ method: 'POST',
1930
+ credentials: 'include',
1931
+ body,
1932
+ headers
1933
+ });
1934
+ // RFC 9449 Section 8.2: new nonce values may be returned in successful responses
1935
+ const newNonce = resp.headers.get('DPoP-Nonce');
1936
+ if (newNonce) {
1937
+ await dpopProvider.updateAuthServerNonce(newNonce);
1938
+ }
1939
+ const tokenResponse = await resp.json();
1940
+ return __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_validateDPoPTokenType).call(this, tokenResponse);
1941
+ };
1942
+ try {
1943
+ const nonce = await dpopProvider.getAuthServerNonce();
1944
+ return await fetchToken(nonce);
1945
+ }
1946
+ catch (ex) {
1947
+ // Check if this is a DPoP nonce error per RFC 9449 Section 8
1948
+ if (ex instanceof NetworkError && ex.response.status === 400) {
1949
+ const dpopNonce = ex.response.headers.get('DPoP-Nonce');
1950
+ if (dpopNonce) {
1951
+ const errorBody = await ex.response.json().catch(() => ({}));
1952
+ if (errorBody.error === 'use_dpop_nonce') {
1953
+ await dpopProvider.updateAuthServerNonce(dpopNonce);
1954
+ return await fetchToken(dpopNonce);
1955
+ }
1999
1956
  }
2000
1957
  }
2001
- throw error;
1958
+ throw ex;
2002
1959
  }
2003
1960
  }, _TideCloak_fetchRefreshToken =
2004
1961
  /**
@@ -2014,49 +1971,23 @@ async function _TideCloak_fetchRefreshToken(url, refreshToken, clientId) {
2014
1971
  ['refresh_token', refreshToken],
2015
1972
  ['client_id', clientId]
2016
1973
  ]);
2017
- /** @type {Record<string, string>} */
2018
- const headers = {};
2019
1974
  if (__classPrivateFieldGet(this, _TideCloak_dpopProvider, "f")) {
2020
- const nonce = await __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f").getAuthServerNonce();
2021
- headers['DPoP'] = await __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f").generateDPoPProof(url, 'POST', undefined, nonce);
1975
+ return await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_fetchDPoPToken).call(this, url, body);
2022
1976
  }
2023
- try {
1977
+ else {
2024
1978
  return await fetchJSON(url, {
2025
1979
  method: 'POST',
2026
1980
  credentials: 'include',
2027
- headers,
2028
1981
  body
2029
1982
  });
2030
1983
  }
2031
- catch (error) {
2032
- // Handle DPoP nonce retry for token endpoint
2033
- if (__classPrivateFieldGet(this, _TideCloak_dpopProvider, "f") && error instanceof NetworkError) {
2034
- const newNonce = error.response.headers.get('DPoP-Nonce');
2035
- if (newNonce) {
2036
- await __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f").updateAuthServerNonce(newNonce);
2037
- headers['DPoP'] = await __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f").generateDPoPProof(url, 'POST', undefined, newNonce);
2038
- return await fetchJSON(url, {
2039
- method: 'POST',
2040
- credentials: 'include',
2041
- headers,
2042
- body
2043
- });
2044
- }
2045
- }
2046
- throw error;
2047
- }
2048
1984
  }, _TideCloak_ensureRequestEnclaveOpen = function _TideCloak_ensureRequestEnclaveOpen() {
2049
- // Never (re)open the enclave without a doken. Post-logout the doken is
2050
- // flushed; reopening a dokenless hidden enclave wedges it with
2051
- // "Expecting doken in request".
2052
1985
  if (!this.requestEnclave || !this.doken)
2053
1986
  return;
2054
1987
  this.requestEnclave.checkEnclaveOpen();
2055
1988
  }, _TideCloak_reloginInteractively =
2056
1989
  /**
2057
- * Trigger a full interactive login redirect. Used as the recovery leg when a
2058
- * silent flow cannot proceed (e.g. the enclave asks for a doken refresh but
2059
- * the doken was flushed at logout). Navigates the whole page to Keycloak.
1990
+ * Full interactive login, used when an enclave needs a doken we don't have.
2060
1991
  * @returns {Promise<void>}
2061
1992
  */
2062
1993
  async function _TideCloak_reloginInteractively() {
@@ -2067,17 +1998,12 @@ async function _TideCloak_reloginInteractively() {
2067
1998
  });
2068
1999
  }, _TideCloak_provideDokenOrRelogin =
2069
2000
  /**
2070
- * Provide the current doken to the enclave, or fall through to interactive
2071
- * login when there is none (post-logout / flushed state). Throwing a bare
2072
- * error here would leave the enclave (and whatever awaits its doken-refresh
2073
- * completion) hanging; instead we redirect to a full interactive login, which
2074
- * recovers cleanly.
2075
2001
  * @returns {Promise<string>}
2076
2002
  */
2077
2003
  async function _TideCloak_provideDokenOrRelogin() {
2078
2004
  await this.ensureTokenReady();
2079
2005
  if (!this.doken) {
2080
- __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.');
2006
+ __classPrivateFieldGet(this, _TideCloak_logWarn, "f").call(this, '[TIDECLOAK] Enclave requested a doken refresh but no doken is present. Falling through to interactive login.');
2081
2007
  await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_reloginInteractively).call(this);
2082
2008
  throw new Error('[TIDECLOAK] No doken found - redirecting to interactive login');
2083
2009
  }
@@ -2096,6 +2022,7 @@ async function _TideCloak_provideDokenOrRelogin() {
2096
2022
  throw new Error('Unable to build voucher URL, realm URL not configured');
2097
2023
  return `${realmUrl}/tidevouchers/fromUserSession?sessionId=${encodeURIComponent(sid)}`;
2098
2024
  }, _TideCloak_setToken = function _TideCloak_setToken(token, refreshToken, idToken, timeLocal, doken) {
2025
+ var _a, _b;
2099
2026
  if (this.tokenTimeoutHandle) {
2100
2027
  clearTimeout(this.tokenTimeoutHandle);
2101
2028
  this.tokenTimeoutHandle = undefined;
@@ -2127,7 +2054,7 @@ async function _TideCloak_provideDokenOrRelogin() {
2127
2054
  if (timeLocal) {
2128
2055
  this.timeSkew = Math.floor(timeLocal / 1000) - this.tokenParsed.iat;
2129
2056
  }
2130
- if (typeof this.timeSkew === 'number') {
2057
+ if (this.timeSkew !== null) {
2131
2058
  __classPrivateFieldGet(this, _TideCloak_logInfo, "f").call(this, '[TIDECLOAK] Estimated time difference between browser and server is ' + this.timeSkew + ' seconds');
2132
2059
  if (this.onTokenExpired) {
2133
2060
  const expiresIn = (this.tokenParsed.exp - (new Date().getTime() / 1000) + this.timeSkew) * 1000;
@@ -2149,38 +2076,26 @@ async function _TideCloak_provideDokenOrRelogin() {
2149
2076
  delete this.resourceAccess;
2150
2077
  this.authenticated = false;
2151
2078
  }
2152
- // Tide doken handling
2153
2079
  if (doken) {
2154
2080
  this.doken = doken;
2155
2081
  this.dokenParsed = decodeToken(doken);
2156
- if (this.requestEnclave && typeof this.requestEnclave.updateDoken === 'function') {
2157
- this.requestEnclave.updateDoken(this.doken);
2158
- }
2082
+ (_b = (_a = this.requestEnclave) === null || _a === void 0 ? void 0 : _a.updateDoken) === null || _b === void 0 ? void 0 : _b.call(_a, this.doken);
2159
2083
  }
2160
2084
  else {
2161
2085
  delete this.doken;
2162
2086
  delete this.dokenParsed;
2163
- // No doken present (e.g. post-logout / flushed state). Do NOT push a
2164
- // "doken refresh" into the enclave: refreshing with an undefined doken
2165
- // wedges the hidden enclave ("Expecting doken in request",
2166
- // TIDE-SWE-UNHANDLED) and blocks the silent re-auth that runs after
2167
- // logout. Instead tear the stale enclave(s) down so (a) no dokenless
2168
- // refresh is ever sent, (b) the persistent user-gesture listener can no
2169
- // longer reopen a dokenless hidden enclave (#ensureRequestEnclaveOpen
2170
- // no-ops once requestEnclave is cleared), and (c) the next authenticated
2171
- // flow re-creates a fresh enclave from a valid doken via
2172
- // initRequestEnclave(). The app is then free to fall through to an
2173
- // interactive login, which recovers cleanly.
2087
+ // A dokenless enclave can't be driven, so close it; the next Tide
2088
+ // operation creates a fresh one from a new doken.
2174
2089
  __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_teardownEnclaves).call(this);
2175
2090
  }
2176
2091
  }, _TideCloak_teardownEnclaves = function _TideCloak_teardownEnclaves() {
2177
- for (const key of ['requestEnclave', 'approvalEnclave']) {
2092
+ var _a;
2093
+ for (const key of /** @type {const} */ (['requestEnclave', 'approvalEnclave'])) {
2178
2094
  const enclave = this[key];
2179
2095
  if (!enclave)
2180
2096
  continue;
2181
2097
  try {
2182
- if (typeof enclave.close === 'function')
2183
- enclave.close();
2098
+ (_a = enclave.close) === null || _a === void 0 ? void 0 : _a.call(enclave);
2184
2099
  }
2185
2100
  catch (error) {
2186
2101
  __classPrivateFieldGet(this, _TideCloak_logWarn, "f").call(this, '[TIDECLOAK] Failed to close ' + key + ': ' + (error instanceof Error ? error.message : error));
@@ -2192,6 +2107,13 @@ async function _TideCloak_provideDokenOrRelogin() {
2192
2107
  return;
2193
2108
  }
2194
2109
  return `${stripTrailingSlash(this.authServerUrl)}/realms/${encodeURIComponent(/** @type {string} */ (this.realm))}`;
2110
+ }, _TideCloak_getIssuerUrl = function _TideCloak_getIssuerUrl() {
2111
+ // For OIDC provider mode, use the issuer from metadata
2112
+ if (this.issuer) {
2113
+ return this.issuer;
2114
+ }
2115
+ // For TideCloak mode, use the realm URL
2116
+ return __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_getRealmUrl).call(this);
2195
2117
  }, _TideCloak_createLogger = function _TideCloak_createLogger(fn) {
2196
2118
  return (message) => {
2197
2119
  if (this.enableLogging) {
@@ -2490,7 +2412,7 @@ function base64ToBytes(base64) {
2490
2412
  const len = binString.length;
2491
2413
  const bytes = new Uint8Array(len);
2492
2414
  for (let i = 0; i < len; i++) {
2493
- bytes[i] = binString.codePointAt(i);
2415
+ bytes[i] = /** @type {number} */ (binString.codePointAt(i));
2494
2416
  }
2495
2417
  return bytes;
2496
2418
  }