@tidecloak/js 0.14.41 → 0.14.43-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 +159 -2095
  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 +156 -2062
  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 +57 -606
  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 +57 -606
  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
@@ -36,18 +36,10 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (
36
36
  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");
37
37
  return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
38
38
  };
39
- 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;
39
+ 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;
40
40
  // MODIFIED: Added dependency to external Tide helper libraries.
41
- import { RequestEnclave, ApprovalEnclave, ApprovalEnclaveNew } from "heimdall-tide";
41
+ import { RequestEnclave, ApprovalEnclaveNew } from "heimdall-tide";
42
42
  const CONTENT_TYPE_JSON = 'application/json';
43
- // Post-logout marker. After an explicit logout there is no session to silently
44
- // resume, so the immediately-following init() must skip the racy silent
45
- // check-sso (whose hidden iframe can net::ERR_ABORTED and wedge the app).
46
- // `logout()` stamps this durable localStorage marker (it survives the logout
47
- // redirect round-trip); init() consumes it exactly once and, on the `check-sso`
48
- // path, settles as not-authenticated so the app shows its logged-out state.
49
- const POST_LOGOUT_MARKER_KEY = 'tide-post-logout';
50
- const POST_LOGOUT_MARKER_TTL_MS = 60000;
51
43
  /**
52
44
  * @typedef {Object} Endpoints
53
45
  * @property {() => string} authorize
@@ -66,8 +58,6 @@ const POST_LOGOUT_MARKER_TTL_MS = 60000;
66
58
  * @property {HTMLIFrameElement=} iframe
67
59
  * @property {string=} iframeOrigin
68
60
  */
69
- import { shouldAttachDpopProof } from './secureFetchPolicy.js';
70
- export { shouldAttachDpopProof } from './secureFetchPolicy.js';
71
61
  export { RequestEnclave, ApprovalEnclave, ApprovalEnclaveNew, PolicySignRequest } from "heimdall-tide";
72
62
  export { Tools, Models } from "@tideorg/js";
73
63
  class TideCloak {
@@ -104,15 +94,6 @@ class TideCloak {
104
94
  );
105
95
  /** @type {import('./tidecloak-dpop.js').DPoPSignatureProvider=} */
106
96
  _TideCloak_dpopProvider.set(this, void 0);
107
- /**
108
- * The last non-matching `Authorization: Bearer …` value `secureFetch` warned
109
- * about, so a consumer stuck in a stale-token state gets ONE warning per stale
110
- * token rather than one per request. See `secureFetch`.
111
- * @type {string|null}
112
- */
113
- _TideCloak_warnedStaleBearer.set(this, null
114
- /** @type {TideCloakConfig} config */
115
- );
116
97
  /** @type {TideCloakConfig} config */
117
98
  _TideCloak_config.set(this, void 0);
118
99
  this.didInitialize = false;
@@ -128,15 +109,6 @@ class TideCloak {
128
109
  this.timeSkew = null;
129
110
  /** @type {boolean} */
130
111
  this.silentCheckSsoFallback = true;
131
- /**
132
- * Max time (ms) to wait for the hidden silent-check-sso iframe to post back
133
- * before treating the attempt as "not authenticated". Guards against the
134
- * post-logout wedge where the check-sso (or the Tide enclave it spins up)
135
- * never responds and init() would otherwise hang forever. Overridable via
136
- * initOptions.silentCheckSsoTimeout.
137
- * @type {number}
138
- */
139
- this.silentCheckSsoTimeout = 10000;
140
112
  /** @type {TideCloakPkceMethod} */
141
113
  this.pkceMethod = 'S256';
142
114
  this.enableLogging = false;
@@ -149,6 +121,7 @@ class TideCloak {
149
121
  */
150
122
  this.init = async (initOptions = {}) => {
151
123
  var _a, _b, _c;
124
+ var _d;
152
125
  if (this.didInitialize) {
153
126
  throw new Error("A 'TideCloak' instance can only be initialized once.");
154
127
  }
@@ -215,9 +188,6 @@ class TideCloak {
215
188
  if (typeof initOptions.silentCheckSsoFallback === 'boolean') {
216
189
  this.silentCheckSsoFallback = initOptions.silentCheckSsoFallback;
217
190
  }
218
- if (typeof initOptions.silentCheckSsoTimeout === 'number' && initOptions.silentCheckSsoTimeout > 0) {
219
- this.silentCheckSsoTimeout = initOptions.silentCheckSsoTimeout;
220
- }
221
191
  if (typeof initOptions.pkceMethod !== 'undefined') {
222
192
  if (initOptions.pkceMethod !== 'S256' && initOptions.pkceMethod !== false) {
223
193
  throw new TypeError(`Invalid value for pkceMethod', expected 'S256' or false but got ${initOptions.pkceMethod}.`);
@@ -236,15 +206,15 @@ class TideCloak {
236
206
  if (typeof initOptions.messageReceiveTimeout === 'number' && initOptions.messageReceiveTimeout > 0) {
237
207
  this.messageReceiveTimeout = initOptions.messageReceiveTimeout;
238
208
  }
239
- if (initOptions.useDPoP) {
240
- this.useDPoP = initOptions.useDPoP;
209
+ if (initOptions.dpopConfig) {
210
+ this.dpopConfig = initOptions.dpopConfig;
241
211
  }
242
212
  await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_loadConfig).call(this);
243
- if (((_a = this.useDPoP) === null || _a === void 0 ? void 0 : _a.mode) === 'strict' && (!this.dpopSigningAlgValuesSupported || this.dpopSigningAlgValuesSupported.length === 0)) {
213
+ if (((_a = this.dpopConfig) === null || _a === void 0 ? void 0 : _a.mode) === 'strict' && (!this.dpopSigningAlgValuesSupported || this.dpopSigningAlgValuesSupported.length === 0)) {
244
214
  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).');
245
215
  }
246
216
  // Initialize DPoP if enabled and server supports it
247
- if (this.useDPoP && ((_b = this.dpopSigningAlgValuesSupported) === null || _b === void 0 ? void 0 : _b.length)) {
217
+ if (this.dpopConfig && ((_b = this.dpopSigningAlgValuesSupported) === null || _b === void 0 ? void 0 : _b.length)) {
248
218
  const issuerUrl = __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_getIssuerUrl).call(this);
249
219
  if (!issuerUrl)
250
220
  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.');
@@ -255,7 +225,8 @@ class TideCloak {
255
225
  issuerUrl: new URL(issuerUrl),
256
226
  clientId: this.clientId,
257
227
  serverSupportedAlgorithms: this.dpopSigningAlgValuesSupported,
258
- requestedAlgorithm: !this.useDPoP.alg ? BrowserSignatureAlgs.ES256 : BrowserSignatureAlgs[this.useDPoP.alg],
228
+ requestedAlgorithm: !this.dpopConfig.alg ? BrowserSignatureAlgs.ES256 : BrowserSignatureAlgs[this.dpopConfig.alg],
229
+ strictStorage: (_d = this.dpopConfig.strictStorage) !== null && _d !== void 0 ? _d : false,
259
230
  getTimeSkew: () => { var _a; return (_a = this.timeSkew) !== null && _a !== void 0 ? _a : 0; }
260
231
  }), "f");
261
232
  await __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f").init();
@@ -264,30 +235,9 @@ class TideCloak {
264
235
  await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_check3pCookiesSupported).call(this);
265
236
  await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_processInit).call(this, initOptions);
266
237
  (_c = this.onReady) === null || _c === void 0 ? void 0 : _c.call(this, this.authenticated);
267
- // Do NOT eagerly open the Tide RequestEnclave during init().
268
- //
269
- // On a post-logout silent re-auth, the doken returned by the silent
270
- // check-sso can be STALE: its session key no longer matches the session the
271
- // hidden enclave cached from the previous login. Opening the hidden enclave
272
- // here makes it sit "waiting for doken refresh" forever - a refresh that
273
- // never comes because no fresh doken exists - which is exactly the
274
- // "[ENCLAVE] Received init but waiting for doken refresh" ... hang that
275
- // wedges the SPA on "Signing you in..." on the second login. (The enclave
276
- // silently WAITS rather than emitting an error, so heimdall's own
277
- // requireReloginCallback recovery never fires.)
278
- //
279
- // Instead we DEFER enclave setup: it is created lazily on the first real
280
- // Tide operation (encrypt / decrypt / approve / signDpopApproval - each
281
- // calls initRequestEnclave()), by which point a fresh interactive login has
282
- // minted a current doken and the enclave can complete its handshake. We
283
- // still register the user-gesture listener so the popup fallback can open
284
- // once an enclave has actually been created; #ensureRequestEnclaveOpen
285
- // no-ops while there is no enclave or no doken.
238
+ // The RequestEnclave is created lazily on the first Tide operation. Popups
239
+ // need a user gesture, so reopen it on click.
286
240
  if (initOptions.setupRequestEnclave) {
287
- if (this.doken) {
288
- __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).');
289
- }
290
- // to get around popups requiring user gestures
291
241
  document.addEventListener('click', () => {
292
242
  __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_ensureRequestEnclaveOpen).call(this);
293
243
  });
@@ -377,8 +327,7 @@ class TideCloak {
377
327
  }
378
328
  }
379
329
  if (__classPrivateFieldGet(this, _TideCloak_dpopProvider, "f")) {
380
- const thumbprint = await __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f").generateJWKThumbprint();
381
- params.append('dpop_jkt', thumbprint);
330
+ params.append('dpop_jkt', await __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f").generateJWKThumbprint());
382
331
  }
383
332
  __classPrivateFieldGet(this, _TideCloak_callbackStorage, "f").add(callbackState);
384
333
  return `${url}?${params.toString()}`;
@@ -390,17 +339,6 @@ class TideCloak {
390
339
  this.logout = async (options) => {
391
340
  var _a;
392
341
  await ((_a = __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f")) === null || _a === void 0 ? void 0 : _a.flush());
393
- // Stamp the post-logout marker so the next init() skips the racy silent
394
- // check-sso instead of trying to resume a session we just ended. See
395
- // POST_LOGOUT_MARKER_KEY.
396
- try {
397
- if (typeof window !== 'undefined' && window.localStorage) {
398
- window.localStorage.setItem(POST_LOGOUT_MARKER_KEY, Date.now().toString());
399
- }
400
- }
401
- catch (e) {
402
- __classPrivateFieldGet(this, _TideCloak_logWarn, "f").call(this, '[TIDE-POSTLOGOUT] could not set marker: ' + (e instanceof Error ? e.message : String(e)));
403
- }
404
342
  return __classPrivateFieldGet(this, _TideCloak_adapter, "f").logout(options);
405
343
  };
406
344
  /**
@@ -582,59 +520,89 @@ class TideCloak {
582
520
  }
583
521
  return await promise;
584
522
  };
585
- this.clearToken = () => {
586
- var _a;
523
+ this.clearToken = async () => {
524
+ var _a, _b, _c;
587
525
  if (this.token) {
526
+ await ((_a = __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f")) === null || _a === void 0 ? void 0 : _a.flush());
527
+ await ((_b = __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f")) === null || _b === void 0 ? void 0 : _b.init());
588
528
  __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_setToken).call(this);
589
- (_a = this.onAuthLogout) === null || _a === void 0 ? void 0 : _a.call(this);
529
+ (_c = this.onAuthLogout) === null || _c === void 0 ? void 0 : _c.call(this);
590
530
  if (this.loginRequired) {
591
- this.login();
531
+ await this.login();
592
532
  }
593
533
  }
594
534
  };
595
- this.secureFetch = async (url, init = {}) => {
596
- var _a, _b;
597
- const dpopProvider = __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f");
598
- if (dpopProvider && this.authenticated && this.token) {
599
- const existingAuth = new Headers(init.headers).get('Authorization');
600
- const isOurBearerToken = shouldAttachDpopProof(existingAuth, this.token);
601
- if (!isOurBearerToken) {
602
- // Quick escape - didn't put this check in first if statement as it's more expensive than other checks.
603
- //
604
- // BEHAVIOUR IS UNCHANGED: a request carrying some OTHER Bearer token is a
605
- // legitimate pass-through (a third-party API), so it goes out as a plain
606
- // fetch with no DPoP proof.
607
- //
608
- // But make it OBSERVABLE. When the caller hands us a Bearer token that is
609
- // almost-but-not-quite ours - i.e. a token this client issued them earlier
610
- // and that has since gone stale because they cached it and missed a refresh
611
- // - this silent downgrade is fatal and mute: a `dpop.bound.access.tokens`
612
- // realm rejects a DPoP-bound token presented as a plain Bearer with a bare
613
- // `401`, and nothing anywhere explains why. One warning per distinct stale
614
- // token (not per request), so a wedged consumer gets a clear signal instead
615
- // of a scrolling wall.
616
- if (typeof existingAuth === 'string' &&
617
- existingAuth.startsWith('Bearer ') &&
618
- existingAuth !== __classPrivateFieldGet(this, _TideCloak_warnedStaleBearer, "f")) {
619
- __classPrivateFieldSet(this, _TideCloak_warnedStaleBearer, existingAuth, "f");
620
- console.warn('[TIDECLOAK] secureFetch: the Authorization header carries a Bearer token that is NOT the ' +
621
- 'token this client currently holds. Sending it as a PLAIN fetch with no DPoP proof. If that ' +
622
- 'token came from this SDK it has gone STALE (the caller cached a copy and missed a refresh) ' +
623
- 'and a DPoP-bound realm will answer 401. Read the token from the SDK at call time ' +
624
- '(IAMService.getToken()) instead of holding a copy, or omit the Authorization header entirely ' +
625
- 'and let secureFetch attach it.');
535
+ this.fetch = async (url, init = {}) => {
536
+ let isRegularFetch = true;
537
+ let options = init; // object to hold all configurations made by extensions
538
+ for (let ext of __classPrivateFieldGet(this, _TideCloak_fetchExtensionChain, "f")) {
539
+ if (ext.use(url, options)) {
540
+ isRegularFetch = false;
541
+ const opts = await ext.configure(url, options);
542
+ if (opts)
543
+ options = opts; // only change if a change was made
544
+ }
545
+ else {
546
+ // this is really important
547
+ // fetchExtensionChain is a CHAIN
548
+ // a request might have dPoP use, then delegation use
549
+ // but it will NEVER have delegation use WIHTOUT dPoP use
550
+ // in that sense, to use tide delegation you need dpop + delegation being used first
551
+ break;
552
+ }
553
+ }
554
+ let response = await fetch(url, options);
555
+ if (isRegularFetch)
556
+ return response;
557
+ let safeguardCounter = __classPrivateFieldGet(this, _TideCloak_fetchExtensionChain, "f").length;
558
+ let handleableErrors = true;
559
+ while (safeguardCounter >= 0 && handleableErrors == true) {
560
+ handleableErrors = false;
561
+ for (let ext of __classPrivateFieldGet(this, _TideCloak_fetchExtensionChain, "f")) {
562
+ // we loop through the full chain since the developer might require tide delegation even though they didn't specify it
563
+ const updatedResponse = await ext.handleResponse(url, options, response);
564
+ if (updatedResponse) {
565
+ // if updated response then we want to continue the look to run all the extension handlers again
566
+ response = updatedResponse;
567
+ handleableErrors = true;
626
568
  }
627
- return fetch(url, init);
628
569
  }
629
- const urlString = url instanceof URL ? url.href : url.toString();
630
- const origin = new URL(urlString).origin;
570
+ safeguardCounter--; // in case the application somehow gets into a loop with dpop + delegation erros
571
+ }
572
+ return response;
573
+ };
574
+ _TideCloak_dPoPFetch.set(this, {
575
+ use: (url, init = {}) => {
576
+ const dpopProvider = __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f");
577
+ if (dpopProvider && this.authenticated && this.token) {
578
+ const existingAuth = new Headers(init.headers).get('Authorization');
579
+ const isOurBearerToken = existingAuth === `Bearer ${this.token}`;
580
+ if (isOurBearerToken)
581
+ return true;
582
+ }
583
+ return false;
584
+ },
585
+ configure: async (url, init = {}) => {
586
+ var _a;
587
+ const dpopProvider = __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f");
588
+ const requestUrl = new URL(String(url), document.baseURI);
589
+ const urlString = requestUrl.href;
590
+ const origin = requestUrl.origin;
631
591
  const method = (_a = init.method) !== null && _a !== void 0 ? _a : 'GET';
632
592
  const resourceNonce = dpopProvider.getResourceServerNonce(origin);
633
593
  const proof = await dpopProvider.generateDPoPProof(urlString, method, this.token, resourceNonce);
634
594
  const headers = new Headers(init.headers);
635
595
  headers.set('Authorization', `DPoP ${this.token}`);
636
596
  headers.set('DPoP', proof);
637
- const resp = await fetch(url, { ...init, headers });
597
+ return { ...init, headers };
598
+ },
599
+ handleResponse: async (url, init, resp) => {
600
+ var _a, _b;
601
+ const dpopProvider = __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f");
602
+ const requestUrl = new URL(String(url), document.baseURI);
603
+ const urlString = requestUrl.href;
604
+ const origin = requestUrl.origin;
605
+ const method = (_a = init.method) !== null && _a !== void 0 ? _a : 'GET';
638
606
  // Check for new nonce in response
639
607
  const newNonce = resp.headers.get('DPoP-Nonce');
640
608
  if (newNonce) {
@@ -645,11 +613,7 @@ class TideCloak {
645
613
  if (resp.status === 401 && newNonce) {
646
614
  const wwwAuth = (_b = resp.headers.get('WWW-Authenticate')) !== null && _b !== void 0 ? _b : '';
647
615
  if (wwwAuth.includes('DPoP') && wwwAuth.includes('error="use_dpop_nonce"')) {
648
- const retryProof = await dpopProvider.generateDPoPProof(urlString, method, this.token, newNonce);
649
- const retryHeaders = new Headers(init.headers);
650
- retryHeaders.set('Authorization', `DPoP ${this.token}`);
651
- retryHeaders.set('DPoP', retryProof);
652
- const retryResp = await fetch(url, { ...init, headers: retryHeaders });
616
+ const retryResp = await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_fetchWithDPoPProof).call(this, url, init, urlString, method, newNonce);
653
617
  // Capture nonce from retry response for future requests
654
618
  const retryNonce = retryResp.headers.get('DPoP-Nonce');
655
619
  if (retryNonce) {
@@ -658,38 +622,60 @@ class TideCloak {
658
622
  return retryResp;
659
623
  }
660
624
  }
661
- return resp;
625
+ return null; // indicate nothing was changed
662
626
  }
663
- else {
664
- // SAFETY INVARIANT: if a DPoP provider exists, this client's tokens are
665
- // sender-constrained (`cnf.jkt`), and a DPoP-bound token presented as a
666
- // plain `Bearer` is INVALID per RFC 9449 - the resource server answers a
667
- // bare `401` that names none of this. The silent fallback below is what
668
- // hid exactly that bug. So when we are about to drop out of the DPoP path
669
- // while STILL carrying an Authorization header, say so loudly - once per
670
- // distinct header value, so a wedged consumer gets a signal rather than a
671
- // scrolling wall.
672
- if (dpopProvider) {
673
- const existingAuth = new Headers(init.headers).get('Authorization');
674
- if (typeof existingAuth === 'string' &&
675
- existingAuth.startsWith('Bearer ') &&
676
- existingAuth !== __classPrivateFieldGet(this, _TideCloak_warnedStaleBearer, "f")) {
677
- __classPrivateFieldSet(this, _TideCloak_warnedStaleBearer, existingAuth, "f");
678
- console.error('[TIDECLOAK] secureFetch: DPoP is ENABLED on this client, but the request is going out as a ' +
679
- 'plain `Authorization: Bearer …` with NO DPoP proof' +
680
- (!this.authenticated
681
- ? ' (the client is not authenticated yet)'
682
- : !this.token
683
- ? ' (the client holds no access token yet)'
684
- : '') +
685
- '. If that token is DPoP-bound (`cnf.jkt`), the server will reject it with a bare 401. ' +
686
- 'Wait for the SDK to be authenticated and read the token from it at call time, or turn DPoP ' +
687
- 'off for this flow (omit `useDPoP` from the config).');
627
+ });
628
+ _TideCloak_delegationFetch.set(this, {
629
+ use: (url, init = {}) => {
630
+ return false;
631
+ },
632
+ configure: async (url, init = {}) => undefined, // cannot configure request withotu explicit server request
633
+ handleResponse: async (url, init, resp) => {
634
+ var _a, _b;
635
+ const dpopProvider = __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f");
636
+ const requestUrl = new URL(String(url), document.baseURI);
637
+ const urlString = requestUrl.href;
638
+ const method = (_a = init.method) !== null && _a !== void 0 ? _a : 'GET';
639
+ const delegationKey = resp.headers.get('Resource-Delegation-Key');
640
+ const delegationChallenge = resp.headers.get('Resource-Delegation-Challenge');
641
+ if (resp.status === 401 && delegationKey && delegationChallenge) {
642
+ const wwwAuth = (_b = resp.headers.get('WWW-Authenticate')) !== null && _b !== void 0 ? _b : '';
643
+ if (wwwAuth.includes('error="DelegationProofNotFound"')) {
644
+ const tokenId = this.tokenParsed.jti;
645
+ if (!tokenId) {
646
+ console.warn('[KEYCLOAK] Refusing DPoP delegation, access token does not include jti claim');
647
+ return;
648
+ }
649
+ const challengeMessage = `resource-token-id-challenge:${tokenId}`;
650
+ let dpopDelegationJwt;
651
+ try {
652
+ dpopDelegationJwt = await dpopProvider.generateResourceDelegation(delegationKey, challengeMessage, delegationChallenge, this.token);
653
+ }
654
+ catch (error) {
655
+ console.warn('[KEYCLOAK] Refusing DPoP delegation, returning original response:', error);
656
+ return;
657
+ }
658
+ if (resp.headers.get('Require-Tide-Delegation') === 'true') {
659
+ try {
660
+ this.initRequestEnclave();
661
+ }
662
+ catch (error) {
663
+ console.warn('[KEYCLOAK] Refusing Tide delegation, no request enclave available:', error);
664
+ }
665
+ // provide tide delegation
666
+ const delegation = bytesToBase64(await this.requestEnclave.requestExecutorWitnessSignature(StringToUint8Array(dpopDelegationJwt.substring(0, dpopDelegationJwt.lastIndexOf('.'))))); // sign only header and payload of delegation jwt, not signature (3rd .)
667
+ const headers = new Headers(init.headers);
668
+ headers.set('Enclave-Resource-Delegation-Signature', delegation);
669
+ init = { ...init, headers };
670
+ }
671
+ const retryResp = await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_fetchWithDPoPDelegationProof).call(this, url, init, urlString, method, dpopDelegationJwt);
672
+ return retryResp;
688
673
  }
689
674
  }
690
- return fetch(url, init);
675
+ return;
691
676
  }
692
- };
677
+ });
678
+ _TideCloak_fetchExtensionChain.set(this, [__classPrivateFieldGet(this, _TideCloak_dPoPFetch, "f"), __classPrivateFieldGet(this, _TideCloak_delegationFetch, "f")]);
693
679
  if (typeof config !== 'string' && !isObject(config)) {
694
680
  throw new Error("The 'TideCloak' constructor must be provided with a configuration object, or a URL to a JSON configuration file.");
695
681
  }
@@ -744,7 +730,7 @@ class TideCloak {
744
730
  signed_client_origin: __classPrivateFieldGet(this, _TideCloak_config, "f")['clientOriginAuth'],
745
731
  vendorId: __classPrivateFieldGet(this, _TideCloak_config, "f").vendorId,
746
732
  voucherURL: __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_getVoucherUrl).call(this),
747
- isRunningLocal: new URL(__classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_getVoucherUrl).call(this)).hostname === "localhost"
733
+ isRunningLocal: new URL(__classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_getVoucherUrl).call(this)).hostname === 'localhost'
748
734
  }).init({
749
735
  doken: this.doken,
750
736
  dokenRefreshCallback: async () => __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_provideDokenOrRelogin).call(this),
@@ -780,7 +766,7 @@ class TideCloak {
780
766
  /**
781
767
  * Role-based encryption via Tide RequestEnclave.
782
768
  * @param {{ data: string | Uint8Array, tags: string[] }[]} toEncrypt
783
- * @param {Uint8Array} decryption_policy If you'd like the data to be protected by a decryption policy
769
+ * @param {Uint8Array | null} [decryption_policy] If you'd like the data to be protected by a decryption policy
784
770
  * @returns {Promise<(string | Uint8Array)[]>}
785
771
  */
786
772
  async encrypt(toEncrypt, decryption_policy = null) {
@@ -794,7 +780,7 @@ class TideCloak {
794
780
  const dataToSend = toEncrypt.map((e) => {
795
781
  if (!isObject(e))
796
782
  throw new Error('All entries must be an object to encrypt');
797
- for (const property of ['data', 'tags']) {
783
+ for (const property of /** @type {const} */ (['data', 'tags'])) {
798
784
  if (!e[property]) {
799
785
  throw new Error(`The configuration object is missing the required '${property}' property.`);
800
786
  }
@@ -808,7 +794,7 @@ class TideCloak {
808
794
  if (typeof tag !== 'string')
809
795
  throw new Error('tags must be provided as an array of strings');
810
796
  if (!decryption_policy) {
811
- // if using standard encryption, check for doken includes default self encrypt roles
797
+ // Without a policy, the user needs the default self-encrypt role for the tag.
812
798
  const tagAccess = this.hasRealmRole(`_tide_${tag}.selfencrypt`);
813
799
  if (!tagAccess)
814
800
  throw new Error(`User has not been given any access to '${tag}'`);
@@ -817,17 +803,17 @@ class TideCloak {
817
803
  return {
818
804
  data: typeof e.data === 'string' ? StringToUint8Array(e.data) : e.data,
819
805
  tags: e.tags,
820
- isRaw: typeof e.data === 'string' ? false : true
806
+ isRaw: typeof e.data !== 'string'
821
807
  };
822
808
  });
823
809
  this.initRequestEnclave();
824
810
  const encrypted = await this.requestEnclave.encrypt(dataToSend, decryption_policy);
825
- return encrypted.map((cipher, i) => (dataToSend[i].isRaw ? cipher : bytesToBase64(cipher)));
811
+ return encrypted.map((/** @type {Uint8Array} */ cipher, /** @type {number} */ i) => (dataToSend[i].isRaw ? cipher : bytesToBase64(cipher)));
826
812
  }
827
813
  /**
828
- * Begin the process of drafting a encryption request
829
- * @param {{ data: string | Uint8Array, tags: string[] }[]} toEncrypt
830
- * @returns
814
+ * Begin drafting an encryption request.
815
+ * @param {{ data: Uint8Array, tags: string[] }[]} toEncrypt
816
+ * @returns {Promise<Uint8Array>}
831
817
  */
832
818
  async draftEncryption(toEncrypt) {
833
819
  await this.ensureTokenReady();
@@ -840,7 +826,7 @@ class TideCloak {
840
826
  const dataToSend = toEncrypt.map((e) => {
841
827
  if (!isObject(e))
842
828
  throw new Error('All entries must be an object to encrypt');
843
- for (const property of ['data', 'tags']) {
829
+ for (const property of /** @type {const} */ (['data', 'tags'])) {
844
830
  if (!e[property]) {
845
831
  throw new Error(`The configuration object is missing the required '${property}' property.`);
846
832
  }
@@ -856,28 +842,27 @@ class TideCloak {
856
842
  }
857
843
  return {
858
844
  data: e.data,
859
- tags: e.tags,
845
+ tags: e.tags
860
846
  };
861
847
  });
862
848
  this.initRequestEnclave();
863
849
  return await this.requestEnclave.draftEncryption(dataToSend);
864
850
  }
865
851
  /**
866
- * Commit a encryption request with a specified policy
852
+ * Commit an encryption request with a specified policy.
867
853
  * @param {Uint8Array} request
868
854
  * @param {Uint8Array} decryption_policy
869
855
  * @returns {Promise<Uint8Array[]>}
870
856
  */
871
857
  async commitEncryption(request, decryption_policy) {
872
- // Commit the encryption request and return data similar to encrypt()
873
858
  await this.ensureTokenReady();
874
859
  this.initRequestEnclave();
875
860
  return await this.requestEnclave.commitEncryption(request, decryption_policy);
876
861
  }
877
862
  /**
878
- * Begin the process of drafting a encryption request
879
- * @param {{ encrypted: string | Uint8Array, tags: string[] }[]} toDecrypt
880
- * @returns {Uint8Array}
863
+ * Begin drafting a decryption request.
864
+ * @param {{ encrypted: Uint8Array, tags: string[] }[]} toDecrypt
865
+ * @returns {Promise<Uint8Array>}
881
866
  */
882
867
  async draftDecryption(toDecrypt) {
883
868
  await this.ensureTokenReady();
@@ -890,7 +875,7 @@ class TideCloak {
890
875
  const dataToSend = toDecrypt.map((e) => {
891
876
  if (!isObject(e))
892
877
  throw new Error('All entries must be an object to decrypt');
893
- for (const property of ['encrypted', 'tags']) {
878
+ for (const property of /** @type {const} */ (['encrypted', 'tags'])) {
894
879
  if (!e[property]) {
895
880
  throw new Error(`The configuration object is missing the required '${property}' property.`);
896
881
  }
@@ -910,17 +895,15 @@ class TideCloak {
910
895
  };
911
896
  });
912
897
  this.initRequestEnclave();
913
- const decryptionRequest = await this.requestEnclave.draftDecryption(dataToSend);
914
- return decryptionRequest;
898
+ return await this.requestEnclave.draftDecryption(dataToSend);
915
899
  }
916
900
  /**
917
- * Commit a decryption request with a specified policy
901
+ * Commit a decryption request with a specified policy.
918
902
  * @param {Uint8Array} request
919
903
  * @param {Uint8Array} decryption_policy
920
904
  * @returns {Promise<Uint8Array[]>}
921
905
  */
922
906
  async commitDecryption(request, decryption_policy) {
923
- // Commit the encryption request and return data similar to encrypt()
924
907
  await this.ensureTokenReady();
925
908
  this.initRequestEnclave();
926
909
  return await this.requestEnclave.commitDecryption(request, decryption_policy);
@@ -946,10 +929,10 @@ class TideCloak {
946
929
  return await this.approvalEnclave.approve(requests);
947
930
  }
948
931
  /**
949
- * Execute a Tide Sign Request
932
+ * Execute a Tide Sign Request.
950
933
  * @param {Uint8Array} request
951
934
  * @param {boolean} [waitForAll=false]
952
- * @returns {Promise<Array>} Array of signatures
935
+ * @returns {Promise<any[]>} Array of signatures
953
936
  */
954
937
  async executeSignRequest(request, waitForAll = false) {
955
938
  await this.ensureTokenReady();
@@ -959,7 +942,7 @@ class TideCloak {
959
942
  /**
960
943
  * Role-based decryption via Tide RequestEnclave.
961
944
  * @param {{ encrypted: string | Uint8Array, tags: string[] }[]} toDecrypt
962
- * @param {Uint8Array} decryption_policy If the data is protected by a decryption policy
945
+ * @param {Uint8Array | null} [decryption_policy] If the data is protected by a decryption policy
963
946
  * @returns {Promise<(string | Uint8Array)[]>}
964
947
  */
965
948
  async decrypt(toDecrypt, decryption_policy = null) {
@@ -973,7 +956,7 @@ class TideCloak {
973
956
  const dataToSend = toDecrypt.map((e) => {
974
957
  if (!isObject(e))
975
958
  throw new Error('All entries must be an object to decrypt');
976
- for (const property of ['encrypted', 'tags']) {
959
+ for (const property of /** @type {const} */ (['encrypted', 'tags'])) {
977
960
  if (!e[property]) {
978
961
  throw new Error(`The configuration object is missing the required '${property}' property.`);
979
962
  }
@@ -987,7 +970,7 @@ class TideCloak {
987
970
  if (typeof tag !== 'string')
988
971
  throw new Error('tags must be provided as an array of strings');
989
972
  if (!decryption_policy) {
990
- // if using standard decryption, check for doken includes default self decrypt roles
973
+ // Without a policy, the user needs the default self-decrypt role for the tag.
991
974
  const tagAccess = this.hasRealmRole(`_tide_${tag}.selfdecrypt`);
992
975
  if (!tagAccess)
993
976
  throw new Error(`User has not been given any access to '${tag}'`);
@@ -996,15 +979,15 @@ class TideCloak {
996
979
  return {
997
980
  encrypted: typeof e.encrypted === 'string' ? base64ToBytes(e.encrypted) : e.encrypted,
998
981
  tags: e.tags,
999
- isRaw: typeof e.encrypted === 'string' ? false : true
982
+ isRaw: typeof e.encrypted !== 'string'
1000
983
  };
1001
984
  });
1002
985
  this.initRequestEnclave();
1003
986
  const decrypted = await this.requestEnclave.decrypt(dataToSend, decryption_policy);
1004
- return decrypted.map((d, i) => (dataToSend[i].isRaw ? d : StringFromUint8Array(d)));
987
+ return decrypted.map((/** @type {Uint8Array} */ d, /** @type {number} */ i) => (dataToSend[i].isRaw ? d : StringFromUint8Array(d)));
1005
988
  }
1006
989
  }
1007
- _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) {
990
+ _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) {
1008
991
  if (type === 'default') {
1009
992
  return __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_loadDefaultAdapter).call(this);
1010
993
  }
@@ -1341,7 +1324,7 @@ async function _TideCloak_loadConfig() {
1341
1324
  */
1342
1325
  async function _TideCloak_setupEndpoints() {
1343
1326
  // Fetch OIDC metadata to get DPoP supported algorithms (only if DPoP is enabled)
1344
- if (this.useDPoP) {
1327
+ if (this.dpopConfig) {
1345
1328
  const issuerUrl = __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_getIssuerUrl).call(this);
1346
1329
  if (!issuerUrl) {
1347
1330
  __classPrivateFieldGet(this, _TideCloak_logWarn, "f").call(this, '[TIDECLOAK] Cannot fetch OIDC metadata: issuer URL is not available');
@@ -1427,11 +1410,6 @@ async function _TideCloak_loadOidcConfig(oidcProvider) {
1427
1410
  return config.userinfo_endpoint;
1428
1411
  }
1429
1412
  };
1430
- }, _TideCloak_getIssuerUrl = function _TideCloak_getIssuerUrl() {
1431
- if (this.issuer)
1432
- return this.issuer;
1433
- const realmUrl = __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_getRealmUrl).call(this);
1434
- return realmUrl || undefined;
1435
1413
  }, _TideCloak_check3pCookiesSupported =
1436
1414
  /**
1437
1415
  * @returns {Promise<void>}
@@ -1504,20 +1482,8 @@ async function _TideCloak_processInit(initOptions) {
1504
1482
  await this.login(options);
1505
1483
  };
1506
1484
  const onLoad = async () => {
1507
- // A fresh marker means we just came back from an explicit logout. The
1508
- // marker is here to skip the silent check-sso, which is racy (its hidden
1509
- // iframe can net::ERR_ABORTED and wedge the app) and pointless: there is
1510
- // no session left to resume. So `check-sso` settles as not-authenticated
1511
- // and the app renders its logged-out state. `login-required` still means
1512
- // what it says, so that path is unchanged. Consumed here either way, so
1513
- // the marker stays one-shot.
1514
- const afterLogout = __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_consumePostLogoutMarker).call(this);
1515
1485
  switch (initOptions.onLoad) {
1516
1486
  case 'check-sso':
1517
- if (afterLogout) {
1518
- __classPrivateFieldGet(this, _TideCloak_logInfo, "f").call(this, '[TIDE-POSTLOGOUT] marker honored in init(); settling as not-authenticated, bypassing silent check-sso');
1519
- return;
1520
- }
1521
1487
  if (__classPrivateFieldGet(this, _TideCloak_loginIframe, "f").enable) {
1522
1488
  await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_setupCheckLoginIframe).call(this);
1523
1489
  const unchanged = await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_checkLoginIframe).call(this);
@@ -1647,38 +1613,13 @@ async function _TideCloak_checkLoginIframe() {
1647
1613
  */
1648
1614
  async function _TideCloak_checkSsoSilently() {
1649
1615
  const iframe = document.createElement('iframe');
1650
- return await new Promise((resolve) => {
1651
- let settled = false;
1652
- /** @type {ReturnType<typeof setTimeout>=} */
1653
- let timer;
1654
- /** @type {ReturnType<typeof setTimeout>=} */
1655
- let loadGraceTimer;
1656
- const cleanup = () => {
1657
- if (timer)
1658
- clearTimeout(timer);
1659
- if (loadGraceTimer)
1660
- clearTimeout(loadGraceTimer);
1661
- window.removeEventListener('message', messageCallback);
1662
- iframe.removeEventListener('error', onIframeError);
1663
- iframe.removeEventListener('load', onIframeLoad);
1664
- if (iframe.parentNode)
1665
- document.body.removeChild(iframe);
1666
- };
1667
- // Every NON-authenticating terminal outcome funnels through here exactly
1668
- // once: onerror / onabort / load-without-message / timeout /
1669
- // createLoginUrl-failure. Resolving (never rejecting) as NOT-authenticated
1670
- // lets init() complete cleanly so the app falls through to interactive
1671
- // login. This is what makes the silent path structurally un-hangable: no
1672
- // matter which DOM event (or none) the aborted iframe produces, one of
1673
- // these fires. `via` is logged so QA can prove which path was taken.
1674
- const settleNotAuthenticated = (via) => {
1675
- if (settled)
1676
- return;
1677
- settled = true;
1678
- __classPrivateFieldGet(this, _TideCloak_logWarn, "f").call(this, '[TIDE-SILENTSSO] terminal=' + via + ' -> not-authenticated (authenticated=' + this.authenticated + ')');
1679
- cleanup();
1680
- resolve();
1681
- };
1616
+ const src = await this.createLoginUrl({ prompt: 'none', redirectUri: this.silentCheckSsoRedirectUri });
1617
+ iframe.setAttribute('src', src);
1618
+ iframe.setAttribute('sandbox', 'allow-storage-access-by-user-activation allow-scripts allow-same-origin');
1619
+ iframe.setAttribute('title', 'keycloak-silent-check-sso');
1620
+ iframe.style.display = 'none';
1621
+ document.body.appendChild(iframe);
1622
+ return await new Promise((resolve, reject) => {
1682
1623
  /**
1683
1624
  * @param {MessageEvent} event
1684
1625
  */
@@ -1686,63 +1627,18 @@ async function _TideCloak_checkSsoSilently() {
1686
1627
  if (event.origin !== window.location.origin || iframe.contentWindow !== event.source) {
1687
1628
  return;
1688
1629
  }
1689
- if (settled)
1690
- return;
1691
- settled = true;
1692
- cleanup();
1630
+ const oauth = __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_parseCallback).call(this, event.data);
1693
1631
  try {
1694
- const oauth = __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_parseCallback).call(this, event.data);
1695
1632
  await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_processCallback).call(this, oauth);
1696
- __classPrivateFieldGet(this, _TideCloak_logInfo, "f").call(this, '[TIDE-SILENTSSO] terminal=message -> processed (authenticated=' + this.authenticated + ')');
1697
1633
  resolve();
1698
1634
  }
1699
1635
  catch (error) {
1700
- // A failed silent callback must NOT reject: rejecting would surface as
1701
- // an init() error / AuthWall wall instead of a clean fall-through to
1702
- // interactive login. Resolve as not-authenticated.
1703
- __classPrivateFieldGet(this, _TideCloak_logWarn, "f").call(this, '[TIDE-SILENTSSO] terminal=message-error -> not-authenticated: ' + (error instanceof Error ? error.message : String(error)));
1704
- resolve();
1636
+ reject(error);
1705
1637
  }
1638
+ document.body.removeChild(iframe);
1639
+ window.removeEventListener('message', messageCallback);
1706
1640
  };
1707
- const onIframeError = () => settleNotAuthenticated('onerror');
1708
- const onIframeLoad = () => {
1709
- // The iframe finished navigating. On the happy path it landed on the
1710
- // real silent-check-sso.html and `messageCallback` settles first. If it
1711
- // loaded an error page, or a cross-origin KC page that never posts, or
1712
- // an aborted navigation that still fired `load`, no message arrives - so
1713
- // arm a short grace timer that settles NOT-authenticated well before the
1714
- // outer ceiling.
1715
- if (settled)
1716
- return;
1717
- const grace = Math.max(250, Math.min(2000, Math.floor(this.silentCheckSsoTimeout / 4)));
1718
- if (loadGraceTimer)
1719
- clearTimeout(loadGraceTimer);
1720
- loadGraceTimer = setTimeout(() => settleNotAuthenticated('load-no-message'), grace);
1721
- };
1722
- // Wire ALL listeners before the navigation starts so no immediate event is
1723
- // missed. `error` covers the browsers that surface a failed iframe
1724
- // navigation as an error/abort event; `load` + the grace timer cover the
1725
- // ones that fire load on an error document; the outer `timer` covers
1726
- // net::ERR_ABORTED that produces NO DOM event at all.
1727
1641
  window.addEventListener('message', messageCallback);
1728
- iframe.addEventListener('error', onIframeError);
1729
- iframe.addEventListener('load', onIframeLoad);
1730
- // Hard ceiling. Created synchronously (NOT after an awaited createLoginUrl)
1731
- // so it is always armed regardless of what the iframe navigation does.
1732
- timer = setTimeout(() => settleNotAuthenticated('timeout'), this.silentCheckSsoTimeout);
1733
- // Build the URL and start the navigation. Failure here must still settle
1734
- // (not reject) so init() stays deterministic.
1735
- this.createLoginUrl({ prompt: 'none', redirectUri: this.silentCheckSsoRedirectUri })
1736
- .then((src) => {
1737
- if (settled)
1738
- return;
1739
- iframe.setAttribute('src', src);
1740
- iframe.setAttribute('sandbox', 'allow-storage-access-by-user-activation allow-scripts allow-same-origin');
1741
- iframe.setAttribute('title', 'keycloak-silent-check-sso');
1742
- iframe.style.display = 'none';
1743
- document.body.appendChild(iframe);
1744
- })
1745
- .catch(() => settleNotAuthenticated('createLoginUrl-error'));
1746
1642
  });
1747
1643
  }, _TideCloak_parseCallback = function _TideCloak_parseCallback(url) {
1748
1644
  const oauth = __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_parseCallbackUrl).call(this, url);
@@ -1877,36 +1773,17 @@ async function _TideCloak_checkSsoSilently() {
1877
1773
  await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_scheduleCheckIframe).call(this);
1878
1774
  }
1879
1775
  }
1880
- }, _TideCloak_consumePostLogoutMarker = function _TideCloak_consumePostLogoutMarker() {
1881
- try {
1882
- if (typeof window === 'undefined' || !window.localStorage)
1883
- return false;
1884
- const raw = window.localStorage.getItem(POST_LOGOUT_MARKER_KEY);
1885
- if (!raw)
1886
- return false;
1887
- window.localStorage.removeItem(POST_LOGOUT_MARKER_KEY);
1888
- const ts = Number(raw);
1889
- const fresh = Number.isFinite(ts) && (Date.now() - ts) < POST_LOGOUT_MARKER_TTL_MS;
1890
- if (!fresh) {
1891
- __classPrivateFieldGet(this, _TideCloak_logWarn, "f").call(this, '[TIDE-POSTLOGOUT] marker present but stale (age > ' + POST_LOGOUT_MARKER_TTL_MS + 'ms); ignoring');
1892
- }
1893
- return fresh;
1894
- }
1895
- catch (e) {
1896
- __classPrivateFieldGet(this, _TideCloak_logWarn, "f").call(this, '[TIDE-POSTLOGOUT] could not read marker: ' + (e instanceof Error ? e.message : String(e)));
1897
- return false;
1898
- }
1899
1776
  }, _TideCloak_fetchAccessToken =
1900
1777
  /**
1901
- * @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}.
1902
- * @property {string} access_token The access token issued by the authorization server.
1903
- * @property {string} token_type The type of the token issued by the authorization server.
1904
- * @property {number} [expires_in] The lifetime in seconds of the access token.
1905
- * @property {string} [refresh_token] The refresh token issued by the authorization server.
1906
- * @property {string} [id_token] The ID token issued by the authorization server, if requested.
1907
- * @property {string} [scope] The scope of the access token.
1908
- * @property {string} [doken] The Tide doken issued by the authorization server.
1909
- */
1778
+ * @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}.
1779
+ * @property {string} access_token The access token issued by the authorization server.
1780
+ * @property {string} token_type The type of the token issued by the authorization server.
1781
+ * @property {number} [expires_in] The lifetime in seconds of the access token.
1782
+ * @property {string} [refresh_token] The refresh token issued by the authorization server.
1783
+ * @property {string} [id_token] The ID token issued by the authorization server, if requested.
1784
+ * @property {string} [scope] The scope of the access token.
1785
+ * @property {string} [doken] The Tide doken issued by the authorization server.
1786
+ */
1910
1787
  /**
1911
1788
  * Fetch the access token from the given URL.
1912
1789
  * @param {string} url
@@ -1926,36 +1803,117 @@ async function _TideCloak_fetchAccessToken(url, code, clientId, redirectUri, pkc
1926
1803
  if (pkceCodeVerifier) {
1927
1804
  body.append('code_verifier', pkceCodeVerifier);
1928
1805
  }
1929
- /** @type {Record<string, string>} */
1930
- const headers = {};
1931
1806
  if (__classPrivateFieldGet(this, _TideCloak_dpopProvider, "f")) {
1932
- const nonce = await __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f").getAuthServerNonce();
1933
- headers['DPoP'] = await __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f").generateDPoPProof(url, 'POST', undefined, nonce);
1807
+ return await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_fetchDPoPToken).call(this, url, body);
1934
1808
  }
1935
- try {
1809
+ else {
1936
1810
  return await fetchJSON(url, {
1937
1811
  method: 'POST',
1938
1812
  credentials: 'include',
1939
- headers,
1940
1813
  body
1941
1814
  });
1942
1815
  }
1943
- catch (error) {
1944
- // Handle DPoP nonce retry for token endpoint
1945
- if (__classPrivateFieldGet(this, _TideCloak_dpopProvider, "f") && error instanceof NetworkError) {
1946
- const newNonce = error.response.headers.get('DPoP-Nonce');
1947
- if (newNonce) {
1948
- await __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f").updateAuthServerNonce(newNonce);
1949
- headers['DPoP'] = await __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f").generateDPoPProof(url, 'POST', undefined, newNonce);
1950
- return await fetchJSON(url, {
1951
- method: 'POST',
1952
- credentials: 'include',
1953
- headers,
1954
- body
1955
- });
1816
+ }, _TideCloak_validateDPoPTokenType = function _TideCloak_validateDPoPTokenType(tokenResponse) {
1817
+ var _a, _b;
1818
+ const tokenType = (_a = tokenResponse.token_type) === null || _a === void 0 ? void 0 : _a.toLowerCase();
1819
+ const hasDPoPProvider = !!__classPrivateFieldGet(this, _TideCloak_dpopProvider, "f");
1820
+ if (hasDPoPProvider && tokenType !== 'dpop') {
1821
+ // We sent a DPoP proof but server returned non-DPoP token
1822
+ if (((_b = this.dpopConfig) === null || _b === void 0 ? void 0 : _b.mode) === 'strict') {
1823
+ throw new Error('DPoP strict mode enabled but server returned non-DPoP token (token_type: ' + tokenResponse.token_type + ')');
1824
+ }
1825
+ // Auto mode: disable DPoP and fall back to Bearer
1826
+ __classPrivateFieldGet(this, _TideCloak_logWarn, "f").call(this, '[TIDECLOAK] Server returned token_type "' + tokenResponse.token_type + '" instead of "DPoP". Disabling DPoP for this session.');
1827
+ __classPrivateFieldSet(this, _TideCloak_dpopProvider, undefined, "f");
1828
+ }
1829
+ return tokenResponse;
1830
+ }, _TideCloak_fetchWithDPoPProof =
1831
+ /**
1832
+ * @param {RequestInfo | URL} url
1833
+ * @param {RequestInit} init
1834
+ * @param {string} urlString
1835
+ * @param {string} method
1836
+ * @param {string} [nonce]
1837
+ * @returns {Promise<Response>}
1838
+ */
1839
+ async function _TideCloak_fetchWithDPoPProof(url, init, urlString, method, nonce) {
1840
+ const dpopProvider = __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f");
1841
+ if (!dpopProvider) {
1842
+ throw new Error('DPoP provider not initialized');
1843
+ }
1844
+ const proof = await dpopProvider.generateDPoPProof(urlString, method, this.token, nonce);
1845
+ const headers = new Headers(init.headers);
1846
+ headers.set('Authorization', `DPoP ${this.token}`);
1847
+ headers.set('DPoP', proof);
1848
+ return fetch(url, { ...init, headers });
1849
+ }, _TideCloak_fetchWithDPoPDelegationProof =
1850
+ /**
1851
+ * @param {RequestInfo | URL} url
1852
+ * @param {RequestInit} init
1853
+ * @param {string} urlString
1854
+ * @param {string} method
1855
+ * @param {string} delegationJwt
1856
+ * @returns {Promise<Response>}
1857
+ */
1858
+ async function _TideCloak_fetchWithDPoPDelegationProof(url, init, urlString, method, delegationJwt) {
1859
+ const dpopProvider = __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f");
1860
+ if (!dpopProvider) {
1861
+ throw new Error('DPoP provider not initialized');
1862
+ }
1863
+ const origin = new URL(urlString).origin;
1864
+ const resourceNonce = dpopProvider.getResourceServerNonce(origin);
1865
+ const headers = new Headers(init.headers);
1866
+ headers.set('Resource-Delegation', delegationJwt);
1867
+ return __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_fetchWithDPoPProof).call(this, url, { ...init, headers }, urlString, method, resourceNonce);
1868
+ }, _TideCloak_fetchDPoPToken =
1869
+ /**
1870
+ * Fetch tokens from the authorization server with DPoP proof attached.
1871
+ * Handles nonce requirements per RFC 9449 Section 8.
1872
+ * @param {string} url
1873
+ * @param {URLSearchParams} body
1874
+ * @returns {Promise<AccessTokenResponse>}
1875
+ */
1876
+ async function _TideCloak_fetchDPoPToken(url, body) {
1877
+ const dpopProvider = __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f");
1878
+ if (!dpopProvider) {
1879
+ throw new Error('DPoP provider not initialized');
1880
+ }
1881
+ const fetchToken = async (nonce) => {
1882
+ const proof = await dpopProvider.generateDPoPProof(url, 'POST', undefined, nonce);
1883
+ const headers = new Headers();
1884
+ headers.set('DPoP', proof);
1885
+ headers.set('Accept', CONTENT_TYPE_JSON);
1886
+ const resp = await fetchWithErrorHandling(url, {
1887
+ method: 'POST',
1888
+ credentials: 'include',
1889
+ body,
1890
+ headers
1891
+ });
1892
+ // RFC 9449 Section 8.2: new nonce values may be returned in successful responses
1893
+ const newNonce = resp.headers.get('DPoP-Nonce');
1894
+ if (newNonce) {
1895
+ await dpopProvider.updateAuthServerNonce(newNonce);
1896
+ }
1897
+ const tokenResponse = await resp.json();
1898
+ return __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_validateDPoPTokenType).call(this, tokenResponse);
1899
+ };
1900
+ try {
1901
+ const nonce = await dpopProvider.getAuthServerNonce();
1902
+ return await fetchToken(nonce);
1903
+ }
1904
+ catch (ex) {
1905
+ // Check if this is a DPoP nonce error per RFC 9449 Section 8
1906
+ if (ex instanceof NetworkError && ex.response.status === 400) {
1907
+ const dpopNonce = ex.response.headers.get('DPoP-Nonce');
1908
+ if (dpopNonce) {
1909
+ const errorBody = await ex.response.json().catch(() => ({}));
1910
+ if (errorBody.error === 'use_dpop_nonce') {
1911
+ await dpopProvider.updateAuthServerNonce(dpopNonce);
1912
+ return await fetchToken(dpopNonce);
1913
+ }
1956
1914
  }
1957
1915
  }
1958
- throw error;
1916
+ throw ex;
1959
1917
  }
1960
1918
  }, _TideCloak_fetchRefreshToken =
1961
1919
  /**
@@ -1971,49 +1929,23 @@ async function _TideCloak_fetchRefreshToken(url, refreshToken, clientId) {
1971
1929
  ['refresh_token', refreshToken],
1972
1930
  ['client_id', clientId]
1973
1931
  ]);
1974
- /** @type {Record<string, string>} */
1975
- const headers = {};
1976
1932
  if (__classPrivateFieldGet(this, _TideCloak_dpopProvider, "f")) {
1977
- const nonce = await __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f").getAuthServerNonce();
1978
- headers['DPoP'] = await __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f").generateDPoPProof(url, 'POST', undefined, nonce);
1933
+ return await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_fetchDPoPToken).call(this, url, body);
1979
1934
  }
1980
- try {
1935
+ else {
1981
1936
  return await fetchJSON(url, {
1982
1937
  method: 'POST',
1983
1938
  credentials: 'include',
1984
- headers,
1985
1939
  body
1986
1940
  });
1987
1941
  }
1988
- catch (error) {
1989
- // Handle DPoP nonce retry for token endpoint
1990
- if (__classPrivateFieldGet(this, _TideCloak_dpopProvider, "f") && error instanceof NetworkError) {
1991
- const newNonce = error.response.headers.get('DPoP-Nonce');
1992
- if (newNonce) {
1993
- await __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f").updateAuthServerNonce(newNonce);
1994
- headers['DPoP'] = await __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f").generateDPoPProof(url, 'POST', undefined, newNonce);
1995
- return await fetchJSON(url, {
1996
- method: 'POST',
1997
- credentials: 'include',
1998
- headers,
1999
- body
2000
- });
2001
- }
2002
- }
2003
- throw error;
2004
- }
2005
1942
  }, _TideCloak_ensureRequestEnclaveOpen = function _TideCloak_ensureRequestEnclaveOpen() {
2006
- // Never (re)open the enclave without a doken. Post-logout the doken is
2007
- // flushed; reopening a dokenless hidden enclave wedges it with
2008
- // "Expecting doken in request".
2009
1943
  if (!this.requestEnclave || !this.doken)
2010
1944
  return;
2011
1945
  this.requestEnclave.checkEnclaveOpen();
2012
1946
  }, _TideCloak_reloginInteractively =
2013
1947
  /**
2014
- * Trigger a full interactive login redirect. Used as the recovery leg when a
2015
- * silent flow cannot proceed (e.g. the enclave asks for a doken refresh but
2016
- * the doken was flushed at logout). Navigates the whole page to Keycloak.
1948
+ * Full interactive login, used when an enclave needs a doken we don't have.
2017
1949
  * @returns {Promise<void>}
2018
1950
  */
2019
1951
  async function _TideCloak_reloginInteractively() {
@@ -2024,17 +1956,12 @@ async function _TideCloak_reloginInteractively() {
2024
1956
  });
2025
1957
  }, _TideCloak_provideDokenOrRelogin =
2026
1958
  /**
2027
- * Provide the current doken to the enclave, or fall through to interactive
2028
- * login when there is none (post-logout / flushed state). Throwing a bare
2029
- * error here would leave the enclave (and whatever awaits its doken-refresh
2030
- * completion) hanging; instead we redirect to a full interactive login, which
2031
- * recovers cleanly.
2032
1959
  * @returns {Promise<string>}
2033
1960
  */
2034
1961
  async function _TideCloak_provideDokenOrRelogin() {
2035
1962
  await this.ensureTokenReady();
2036
1963
  if (!this.doken) {
2037
- __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.');
1964
+ __classPrivateFieldGet(this, _TideCloak_logWarn, "f").call(this, '[TIDECLOAK] Enclave requested a doken refresh but no doken is present. Falling through to interactive login.');
2038
1965
  await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_reloginInteractively).call(this);
2039
1966
  throw new Error('[TIDECLOAK] No doken found - redirecting to interactive login');
2040
1967
  }
@@ -2053,6 +1980,7 @@ async function _TideCloak_provideDokenOrRelogin() {
2053
1980
  throw new Error('Unable to build voucher URL, realm URL not configured');
2054
1981
  return `${realmUrl}/tidevouchers/fromUserSession?sessionId=${encodeURIComponent(sid)}`;
2055
1982
  }, _TideCloak_setToken = function _TideCloak_setToken(token, refreshToken, idToken, timeLocal, doken) {
1983
+ var _a, _b;
2056
1984
  if (this.tokenTimeoutHandle) {
2057
1985
  clearTimeout(this.tokenTimeoutHandle);
2058
1986
  this.tokenTimeoutHandle = undefined;
@@ -2084,7 +2012,7 @@ async function _TideCloak_provideDokenOrRelogin() {
2084
2012
  if (timeLocal) {
2085
2013
  this.timeSkew = Math.floor(timeLocal / 1000) - this.tokenParsed.iat;
2086
2014
  }
2087
- if (typeof this.timeSkew === 'number') {
2015
+ if (this.timeSkew !== null) {
2088
2016
  __classPrivateFieldGet(this, _TideCloak_logInfo, "f").call(this, '[TIDECLOAK] Estimated time difference between browser and server is ' + this.timeSkew + ' seconds');
2089
2017
  if (this.onTokenExpired) {
2090
2018
  const expiresIn = (this.tokenParsed.exp - (new Date().getTime() / 1000) + this.timeSkew) * 1000;
@@ -2106,38 +2034,26 @@ async function _TideCloak_provideDokenOrRelogin() {
2106
2034
  delete this.resourceAccess;
2107
2035
  this.authenticated = false;
2108
2036
  }
2109
- // Tide doken handling
2110
2037
  if (doken) {
2111
2038
  this.doken = doken;
2112
2039
  this.dokenParsed = decodeToken(doken);
2113
- if (this.requestEnclave && typeof this.requestEnclave.updateDoken === 'function') {
2114
- this.requestEnclave.updateDoken(this.doken);
2115
- }
2040
+ (_b = (_a = this.requestEnclave) === null || _a === void 0 ? void 0 : _a.updateDoken) === null || _b === void 0 ? void 0 : _b.call(_a, this.doken);
2116
2041
  }
2117
2042
  else {
2118
2043
  delete this.doken;
2119
2044
  delete this.dokenParsed;
2120
- // No doken present (e.g. post-logout / flushed state). Do NOT push a
2121
- // "doken refresh" into the enclave: refreshing with an undefined doken
2122
- // wedges the hidden enclave ("Expecting doken in request",
2123
- // TIDE-SWE-UNHANDLED) and blocks the silent re-auth that runs after
2124
- // logout. Instead tear the stale enclave(s) down so (a) no dokenless
2125
- // refresh is ever sent, (b) the persistent user-gesture listener can no
2126
- // longer reopen a dokenless hidden enclave (#ensureRequestEnclaveOpen
2127
- // no-ops once requestEnclave is cleared), and (c) the next authenticated
2128
- // flow re-creates a fresh enclave from a valid doken via
2129
- // initRequestEnclave(). The app is then free to fall through to an
2130
- // interactive login, which recovers cleanly.
2045
+ // A dokenless enclave can't be driven, so close it; the next Tide
2046
+ // operation creates a fresh one from a new doken.
2131
2047
  __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_teardownEnclaves).call(this);
2132
2048
  }
2133
2049
  }, _TideCloak_teardownEnclaves = function _TideCloak_teardownEnclaves() {
2134
- for (const key of ['requestEnclave', 'approvalEnclave']) {
2050
+ var _a;
2051
+ for (const key of /** @type {const} */ (['requestEnclave', 'approvalEnclave'])) {
2135
2052
  const enclave = this[key];
2136
2053
  if (!enclave)
2137
2054
  continue;
2138
2055
  try {
2139
- if (typeof enclave.close === 'function')
2140
- enclave.close();
2056
+ (_a = enclave.close) === null || _a === void 0 ? void 0 : _a.call(enclave);
2141
2057
  }
2142
2058
  catch (error) {
2143
2059
  __classPrivateFieldGet(this, _TideCloak_logWarn, "f").call(this, '[TIDECLOAK] Failed to close ' + key + ': ' + (error instanceof Error ? error.message : error));
@@ -2149,6 +2065,13 @@ async function _TideCloak_provideDokenOrRelogin() {
2149
2065
  return;
2150
2066
  }
2151
2067
  return `${stripTrailingSlash(this.authServerUrl)}/realms/${encodeURIComponent(/** @type {string} */ (this.realm))}`;
2068
+ }, _TideCloak_getIssuerUrl = function _TideCloak_getIssuerUrl() {
2069
+ // For OIDC provider mode, use the issuer from metadata
2070
+ if (this.issuer) {
2071
+ return this.issuer;
2072
+ }
2073
+ // For TideCloak mode, use the realm URL
2074
+ return __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_getRealmUrl).call(this);
2152
2075
  }, _TideCloak_createLogger = function _TideCloak_createLogger(fn) {
2153
2076
  return (message) => {
2154
2077
  if (this.enableLogging) {
@@ -2447,7 +2370,7 @@ function base64ToBytes(base64) {
2447
2370
  const len = binString.length;
2448
2371
  const bytes = new Uint8Array(len);
2449
2372
  for (let i = 0; i < len; i++) {
2450
- bytes[i] = binString.codePointAt(i);
2373
+ bytes[i] = /** @type {number} */ (binString.codePointAt(i));
2451
2374
  }
2452
2375
  return bytes;
2453
2376
  }