@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.
- 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 +159 -2095
- 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 +156 -2062
- 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 +57 -606
- 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 +57 -606
- 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
|
@@ -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,
|
|
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,
|
|
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.
|
|
240
|
-
this.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
-
(
|
|
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.
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
//
|
|
607
|
-
//
|
|
608
|
-
//
|
|
609
|
-
//
|
|
610
|
-
//
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
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
|
-
|
|
630
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
625
|
+
return null; // indicate nothing was changed
|
|
662
626
|
}
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
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
|
|
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 ===
|
|
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
|
-
//
|
|
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
|
|
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
|
|
829
|
-
* @param {{ data:
|
|
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
|
|
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
|
|
879
|
-
* @param {{ encrypted:
|
|
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
|
-
|
|
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<
|
|
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
|
-
//
|
|
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
|
|
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(),
|
|
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.
|
|
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
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
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
|
|
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 (
|
|
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
|
-
|
|
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
|
-
//
|
|
2121
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|