@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.
- package/README.md +32 -47
- package/dist/cjs/lib/tidecloak-dpop.js +151 -42
- package/dist/cjs/lib/tidecloak-dpop.js.map +1 -1
- package/dist/cjs/lib/tidecloak.js +300 -378
- package/dist/cjs/lib/tidecloak.js.map +1 -1
- package/dist/cjs/src/AdminAPI.js +12 -19
- package/dist/cjs/src/AdminAPI.js.map +1 -1
- package/dist/cjs/src/IAMService.js +155 -2098
- package/dist/cjs/src/IAMService.js.map +1 -1
- package/dist/cjs/src/index.js +1 -7
- package/dist/cjs/src/index.js.map +1 -1
- package/dist/esm/lib/tidecloak-dpop.js +151 -42
- package/dist/esm/lib/tidecloak-dpop.js.map +1 -1
- package/dist/esm/lib/tidecloak.js +300 -377
- package/dist/esm/lib/tidecloak.js.map +1 -1
- package/dist/esm/src/AdminAPI.js +3 -7
- package/dist/esm/src/AdminAPI.js.map +1 -1
- package/dist/esm/src/IAMService.js +152 -2065
- package/dist/esm/src/IAMService.js.map +1 -1
- package/dist/esm/src/index.js +1 -7
- package/dist/esm/src/index.js.map +1 -1
- package/dist/types/AdminAPI.d.ts +1 -1
- package/dist/types/IAMService.d.ts +52 -603
- package/dist/types/index.d.ts +1 -11
- package/dist/types/lib/tidecloak-dpop.d.ts +19 -3
- package/dist/types/lib/tidecloak.d.ts +46 -38
- package/dist/types/src/AdminAPI.d.ts +1 -1
- package/dist/types/src/IAMService.d.ts +52 -603
- package/dist/types/src/index.d.ts +1 -11
- package/docs/FRONT_CHANNEL.md +55 -55
- package/lib/README.md +10 -14
- package/package.json +4 -25
- package/dist/cjs/lib/secureFetchPolicy.js +0 -45
- package/dist/cjs/lib/secureFetchPolicy.js.map +0 -1
- package/dist/cjs/src/dpopRoute.js +0 -30
- package/dist/cjs/src/dpopRoute.js.map +0 -1
- package/dist/cjs/src/dpopServer.js +0 -77
- package/dist/cjs/src/dpopServer.js.map +0 -1
- package/dist/cjs/src/generated/dpopAuthAsset.js +0 -7
- package/dist/cjs/src/generated/dpopAuthAsset.js.map +0 -1
- package/dist/cjs/src/policy-react.js +0 -51
- package/dist/cjs/src/policy-react.js.map +0 -1
- package/dist/cjs/src/types.js +0 -6
- package/dist/cjs/src/types.js.map +0 -1
- package/dist/cjs/src/utils/dpopAuthPage.js +0 -69
- package/dist/cjs/src/utils/dpopAuthPage.js.map +0 -1
- package/dist/cjs/src/utils/fetch.js +0 -39
- package/dist/cjs/src/utils/fetch.js.map +0 -1
- package/dist/cjs/src/utils/index.js +0 -17
- package/dist/cjs/src/utils/index.js.map +0 -1
- package/dist/cjs/src/utils/initOptions.js +0 -78
- package/dist/cjs/src/utils/initOptions.js.map +0 -1
- package/dist/cjs/src/utils/pkce.js +0 -48
- package/dist/cjs/src/utils/pkce.js.map +0 -1
- package/dist/cjs/src/utils/silentCheckSso.js +0 -138
- package/dist/cjs/src/utils/silentCheckSso.js.map +0 -1
- package/dist/cjs/src/vite.js +0 -43
- package/dist/cjs/src/vite.js.map +0 -1
- package/dist/esm/lib/secureFetchPolicy.js +0 -42
- package/dist/esm/lib/secureFetchPolicy.js.map +0 -1
- package/dist/esm/src/dpopRoute.js +0 -27
- package/dist/esm/src/dpopRoute.js.map +0 -1
- package/dist/esm/src/dpopServer.js +0 -68
- package/dist/esm/src/dpopServer.js.map +0 -1
- package/dist/esm/src/generated/dpopAuthAsset.js +0 -4
- package/dist/esm/src/generated/dpopAuthAsset.js.map +0 -1
- package/dist/esm/src/policy-react.js +0 -45
- package/dist/esm/src/policy-react.js.map +0 -1
- package/dist/esm/src/types.js +0 -5
- package/dist/esm/src/types.js.map +0 -1
- package/dist/esm/src/utils/dpopAuthPage.js +0 -63
- package/dist/esm/src/utils/dpopAuthPage.js.map +0 -1
- package/dist/esm/src/utils/fetch.js +0 -36
- package/dist/esm/src/utils/fetch.js.map +0 -1
- package/dist/esm/src/utils/index.js +0 -6
- package/dist/esm/src/utils/index.js.map +0 -1
- package/dist/esm/src/utils/initOptions.js +0 -75
- package/dist/esm/src/utils/initOptions.js.map +0 -1
- package/dist/esm/src/utils/pkce.js +0 -43
- package/dist/esm/src/utils/pkce.js.map +0 -1
- package/dist/esm/src/utils/silentCheckSso.js +0 -134
- package/dist/esm/src/utils/silentCheckSso.js.map +0 -1
- package/dist/esm/src/vite.js +0 -40
- package/dist/esm/src/vite.js.map +0 -1
- package/dist/types/dpopRoute.d.ts +0 -11
- package/dist/types/dpopServer.d.ts +0 -35
- package/dist/types/generated/dpopAuthAsset.d.ts +0 -2
- package/dist/types/lib/secureFetchPolicy.d.ts +0 -37
- package/dist/types/policy-react.d.ts +0 -40
- package/dist/types/src/dpopRoute.d.ts +0 -11
- package/dist/types/src/dpopServer.d.ts +0 -35
- package/dist/types/src/generated/dpopAuthAsset.d.ts +0 -2
- package/dist/types/src/policy-react.d.ts +0 -40
- package/dist/types/src/types.d.ts +0 -114
- package/dist/types/src/utils/dpopAuthPage.d.ts +0 -22
- package/dist/types/src/utils/fetch.d.ts +0 -11
- package/dist/types/src/utils/index.d.ts +0 -5
- package/dist/types/src/utils/initOptions.d.ts +0 -69
- package/dist/types/src/utils/pkce.d.ts +0 -24
- package/dist/types/src/utils/silentCheckSso.d.ts +0 -82
- package/dist/types/src/vite.d.ts +0 -13
- package/dist/types/types.d.ts +0 -114
- package/dist/types/utils/dpopAuthPage.d.ts +0 -22
- package/dist/types/utils/fetch.d.ts +0 -11
- package/dist/types/utils/index.d.ts +0 -5
- package/dist/types/utils/initOptions.d.ts +0 -69
- package/dist/types/utils/pkce.d.ts +0 -24
- package/dist/types/utils/silentCheckSso.d.ts +0 -82
- package/dist/types/vite.d.ts +0 -13
- package/docs/HYBRID_MODE.md +0 -185
- 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,
|
|
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 =
|
|
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.
|
|
283
|
-
this.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
-
(
|
|
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.
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
//
|
|
650
|
-
//
|
|
651
|
-
//
|
|
652
|
-
//
|
|
653
|
-
//
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
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
|
-
|
|
673
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
667
|
+
return null; // indicate nothing was changed
|
|
705
668
|
}
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
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
|
|
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 ===
|
|
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
|
-
//
|
|
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
|
|
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
|
|
872
|
-
* @param {{ data:
|
|
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
|
|
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
|
|
922
|
-
* @param {{ encrypted:
|
|
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
|
-
|
|
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<
|
|
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
|
-
//
|
|
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
|
|
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(),
|
|
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.
|
|
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
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
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
|
|
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 (
|
|
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
|
-
|
|
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
|
-
//
|
|
2164
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|