@tidecloak/js 0.13.14 → 0.13.16

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.
@@ -1,3 +1,6 @@
1
+ /**
2
+ * @import {Acr, TideCloakAccountOptions, TideCloakAdapter, TideCloakConfig, TideCloakError, TideCloakFlow, TideCloakInitOptions, TideCloakLoginOptions, TideCloakLogoutOptions, TideCloakPkceMethod, TideCloakProfile, TideCloakRegisterOptions, TideCloakResourceAccess, TideCloakResponseMode, TideCloakResponseType, TideCloakRoles, TideCloakTokenParsed, OpenIdProviderMetadata} from "./tidecloak.js"
3
+ */
1
4
  /*
2
5
  * Copyright 2016 Red Hat, Inc. and/or its affiliates
3
6
  * and other contributors as indicated by the @author tags.
@@ -33,7 +36,7 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (
33
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");
34
37
  return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
35
38
  };
36
- var _TideCloak_instances, _TideCloak_refreshQueue, _TideCloak_adapter, _TideCloak_useNonce, _TideCloak_callbackStorage, _TideCloak_logInfo, _TideCloak_logWarn, _TideCloak_loginIframe, _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_getVoucherUrl, _TideCloak_setToken, _TideCloak_getRealmUrl, _TideCloak_createLogger, _LocalStorage_instances, _LocalStorage_clearInvalidValues, _LocalStorage_clearAllValues, _LocalStorage_getStoredEntries, _LocalStorage_parseExpiry, _CookieStorage_instances, _CookieStorage_getCookie, _CookieStorage_setCookie, _CookieStorage_cookieExpiration;
39
+ var _TideCloak_instances, _TideCloak_refreshQueue, _TideCloak_adapter, _TideCloak_useNonce, _TideCloak_callbackStorage, _TideCloak_logInfo, _TideCloak_logWarn, _TideCloak_loginIframe, _TideCloak_dpopProvider, _TideCloak_config, _TideCloak_loadAdapter, _TideCloak_loadDefaultAdapter, _TideCloak_loadCordovaAdapter, _TideCloak_loadCordovaNativeAdapter, _TideCloak_loadConfig, _TideCloak_setupEndpoints, _TideCloak_loadOidcConfig, _TideCloak_setupOidcEndpoints, _TideCloak_getIssuerUrl, _TideCloak_check3pCookiesSupported, _TideCloak_processInit, _TideCloak_setupCheckLoginIframe, _TideCloak_checkLoginIframe, _TideCloak_checkSsoSilently, _TideCloak_parseCallback, _TideCloak_parseCallbackUrl, _TideCloak_parseCallbackParams, _TideCloak_processCallback, _TideCloak_scheduleCheckIframe, _TideCloak_fetchAccessToken, _TideCloak_fetchRefreshToken, _TideCloak_ensureRequestEnclaveOpen, _TideCloak_getVoucherUrl, _TideCloak_setToken, _TideCloak_getRealmUrl, _TideCloak_createLogger, _LocalStorage_instances, _LocalStorage_clearInvalidValues, _LocalStorage_clearAllValues, _LocalStorage_getStoredEntries, _LocalStorage_parseExpiry, _CookieStorage_instances, _CookieStorage_getCookie, _CookieStorage_setCookie, _CookieStorage_cookieExpiration;
37
40
  // MODIFIED: Added dependency to external Tide helper libraries.
38
41
  import { RequestEnclave, ApprovalEnclave, ApprovalEnclaveNew } from "heimdall-tide";
39
42
  const CONTENT_TYPE_JSON = 'application/json';
@@ -58,15 +61,15 @@ const CONTENT_TYPE_JSON = 'application/json';
58
61
  export { RequestEnclave, ApprovalEnclave, ApprovalEnclaveNew, TideMemory, BaseTideRequest, PolicySignRequest, Policy, PolicyParameters } from "heimdall-tide";
59
62
  class TideCloak {
60
63
  /**
61
- * @param {KeycloakConfig} config
64
+ * @param {TideCloakConfig} config
62
65
  */
63
66
  constructor(config) {
64
67
  _TideCloak_instances.add(this);
65
68
  /** @type {Pick<PromiseWithResolvers<boolean>, 'resolve' | 'reject'>[]} */
66
69
  _TideCloak_refreshQueue.set(this, []
67
- /** @type {KeycloakAdapter} */
70
+ /** @type {TideCloakAdapter} */
68
71
  );
69
- /** @type {KeycloakAdapter} */
72
+ /** @type {TideCloakAdapter} */
70
73
  _TideCloak_adapter.set(this, void 0);
71
74
  /** @type {boolean} */
72
75
  _TideCloak_useNonce.set(this, true
@@ -84,40 +87,501 @@ class TideCloak {
84
87
  callbackList: [],
85
88
  interval: 5
86
89
  }
87
- /** @type {KeycloakConfig} config */
90
+ /** @type {import('./tidecloak-dpop.js').DPoPSignatureProvider=} */
88
91
  );
89
- /** @type {KeycloakConfig} config */
92
+ /** @type {import('./tidecloak-dpop.js').DPoPSignatureProvider=} */
93
+ _TideCloak_dpopProvider.set(this, void 0);
94
+ /** @type {TideCloakConfig} config */
90
95
  _TideCloak_config.set(this, void 0);
91
96
  this.didInitialize = false;
92
97
  this.authenticated = false;
93
98
  this.loginRequired = false;
94
- /** @type {KeycloakResponseMode} */
99
+ /** @type {TideCloakResponseMode} */
95
100
  this.responseMode = 'fragment';
96
- /** @type {KeycloakResponseType} */
101
+ /** @type {TideCloakResponseType} */
97
102
  this.responseType = 'code';
98
- /** @type {KeycloakFlow} */
103
+ /** @type {TideCloakFlow} */
99
104
  this.flow = 'standard';
105
+ /** @type {number?} */
106
+ this.timeSkew = null;
100
107
  /** @type {boolean} */
101
108
  this.silentCheckSsoFallback = true;
102
- /** @type {KeycloakPkceMethod} */
109
+ /** @type {TideCloakPkceMethod} */
103
110
  this.pkceMethod = 'S256';
104
111
  this.enableLogging = false;
105
112
  /** @type {'GET' | 'POST'} */
106
113
  this.logoutMethod = 'GET';
107
114
  this.messageReceiveTimeout = 10000;
115
+ /**
116
+ * @param {TideCloakInitOptions} initOptions
117
+ * @returns {Promise<boolean>}
118
+ */
119
+ this.init = async (initOptions = {}) => {
120
+ var _a, _b, _c;
121
+ if (this.didInitialize) {
122
+ throw new Error("A 'TideCloak' instance can only be initialized once.");
123
+ }
124
+ this.didInitialize = true;
125
+ __classPrivateFieldSet(this, _TideCloak_callbackStorage, createCallbackStorage(), "f");
126
+ const adapters = ['default', 'cordova', 'cordova-native'];
127
+ if (typeof initOptions.adapter === 'string' && adapters.includes(initOptions.adapter)) {
128
+ __classPrivateFieldSet(this, _TideCloak_adapter, __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_loadAdapter).call(this, initOptions.adapter), "f");
129
+ }
130
+ else if (typeof initOptions.adapter === 'object') {
131
+ __classPrivateFieldSet(this, _TideCloak_adapter, initOptions.adapter, "f");
132
+ }
133
+ else if ('Cordova' in window || 'cordova' in window) {
134
+ __classPrivateFieldSet(this, _TideCloak_adapter, __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_loadAdapter).call(this, 'cordova'), "f");
135
+ }
136
+ else {
137
+ __classPrivateFieldSet(this, _TideCloak_adapter, __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_loadAdapter).call(this, 'default'), "f");
138
+ }
139
+ if (typeof initOptions.useNonce !== 'undefined') {
140
+ __classPrivateFieldSet(this, _TideCloak_useNonce, initOptions.useNonce, "f");
141
+ }
142
+ if (typeof initOptions.checkLoginIframe !== 'undefined') {
143
+ __classPrivateFieldGet(this, _TideCloak_loginIframe, "f").enable = initOptions.checkLoginIframe;
144
+ }
145
+ if (initOptions.checkLoginIframeInterval) {
146
+ __classPrivateFieldGet(this, _TideCloak_loginIframe, "f").interval = initOptions.checkLoginIframeInterval;
147
+ }
148
+ if (initOptions.onLoad === 'login-required') {
149
+ this.loginRequired = true;
150
+ }
151
+ if (initOptions.responseMode) {
152
+ if (initOptions.responseMode === 'query' || initOptions.responseMode === 'fragment') {
153
+ this.responseMode = initOptions.responseMode;
154
+ }
155
+ else {
156
+ throw new Error('Invalid value for responseMode');
157
+ }
158
+ }
159
+ if (initOptions.flow) {
160
+ switch (initOptions.flow) {
161
+ case 'standard':
162
+ this.responseType = 'code';
163
+ break;
164
+ case 'implicit':
165
+ this.responseType = 'id_token token';
166
+ break;
167
+ case 'hybrid':
168
+ this.responseType = 'code id_token token';
169
+ break;
170
+ default:
171
+ throw new Error('Invalid value for flow');
172
+ }
173
+ this.flow = initOptions.flow;
174
+ }
175
+ if (typeof initOptions.timeSkew === 'number') {
176
+ this.timeSkew = initOptions.timeSkew;
177
+ }
178
+ if (initOptions.redirectUri) {
179
+ this.redirectUri = initOptions.redirectUri;
180
+ }
181
+ if (initOptions.silentCheckSsoRedirectUri) {
182
+ this.silentCheckSsoRedirectUri = initOptions.silentCheckSsoRedirectUri;
183
+ }
184
+ if (typeof initOptions.silentCheckSsoFallback === 'boolean') {
185
+ this.silentCheckSsoFallback = initOptions.silentCheckSsoFallback;
186
+ }
187
+ if (typeof initOptions.pkceMethod !== 'undefined') {
188
+ if (initOptions.pkceMethod !== 'S256' && initOptions.pkceMethod !== false) {
189
+ throw new TypeError(`Invalid value for pkceMethod', expected 'S256' or false but got ${initOptions.pkceMethod}.`);
190
+ }
191
+ this.pkceMethod = initOptions.pkceMethod;
192
+ }
193
+ if (typeof initOptions.enableLogging === 'boolean') {
194
+ this.enableLogging = initOptions.enableLogging;
195
+ }
196
+ if (initOptions.logoutMethod === 'POST') {
197
+ this.logoutMethod = 'POST';
198
+ }
199
+ if (typeof initOptions.scope === 'string') {
200
+ this.scope = initOptions.scope;
201
+ }
202
+ if (typeof initOptions.messageReceiveTimeout === 'number' && initOptions.messageReceiveTimeout > 0) {
203
+ this.messageReceiveTimeout = initOptions.messageReceiveTimeout;
204
+ }
205
+ if (initOptions.useDPoP) {
206
+ this.useDPoP = initOptions.useDPoP;
207
+ }
208
+ await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_loadConfig).call(this);
209
+ if (((_a = this.useDPoP) === null || _a === void 0 ? void 0 : _a.mode) === 'strict' && (!this.dpopSigningAlgValuesSupported || this.dpopSigningAlgValuesSupported.length === 0)) {
210
+ 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).');
211
+ }
212
+ // Initialize DPoP if enabled and server supports it
213
+ if (this.useDPoP && ((_b = this.dpopSigningAlgValuesSupported) === null || _b === void 0 ? void 0 : _b.length)) {
214
+ const issuerUrl = __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_getIssuerUrl).call(this);
215
+ if (!issuerUrl)
216
+ 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.');
217
+ if (!this.clientId)
218
+ throw new Error('Cannot initialize DPoP: clientId is not configured.');
219
+ const { DPoPSignatureProvider, BrowserSignatureAlgs } = await import('./tidecloak-dpop.js');
220
+ __classPrivateFieldSet(this, _TideCloak_dpopProvider, new DPoPSignatureProvider({
221
+ issuerUrl: new URL(issuerUrl),
222
+ clientId: this.clientId,
223
+ serverSupportedAlgorithms: this.dpopSigningAlgValuesSupported,
224
+ requestedAlgorithm: !this.useDPoP.alg ? BrowserSignatureAlgs.ES256 : BrowserSignatureAlgs[this.useDPoP.alg]
225
+ }), "f");
226
+ await __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f").init();
227
+ __classPrivateFieldGet(this, _TideCloak_logInfo, "f").call(this, '[TIDECLOAK] DPoP initialized');
228
+ }
229
+ await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_check3pCookiesSupported).call(this);
230
+ await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_processInit).call(this, initOptions);
231
+ (_c = this.onReady) === null || _c === void 0 ? void 0 : _c.call(this, this.authenticated);
232
+ // initialize request enclave if authenticated
233
+ if (this.token)
234
+ this.initRequestEnclave();
235
+ // to get around popups requiring user gestures
236
+ document.addEventListener('click', () => {
237
+ __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_ensureRequestEnclaveOpen).call(this);
238
+ });
239
+ return this.authenticated;
240
+ };
241
+ /**
242
+ * @param {TideCloakLoginOptions} [options]
243
+ * @returns {Promise<void>}
244
+ */
245
+ this.login = (options) => {
246
+ return __classPrivateFieldGet(this, _TideCloak_adapter, "f").login(options);
247
+ };
248
+ /**
249
+ * @param {TideCloakLoginOptions} [options]
250
+ * @returns {Promise<string>}
251
+ */
252
+ this.createLoginUrl = async (options) => {
253
+ const state = createUUID();
254
+ const nonce = createUUID();
255
+ const redirectUri = __classPrivateFieldGet(this, _TideCloak_adapter, "f").redirectUri(options);
256
+ /** @type {CallbackState} */
257
+ const callbackState = {
258
+ state,
259
+ nonce,
260
+ redirectUri,
261
+ loginOptions: options
262
+ };
263
+ if (options === null || options === void 0 ? void 0 : options.prompt) {
264
+ callbackState.prompt = options.prompt;
265
+ }
266
+ const url = (options === null || options === void 0 ? void 0 : options.action) === 'register'
267
+ ? this.endpoints.register()
268
+ : this.endpoints.authorize();
269
+ let scope = (options === null || options === void 0 ? void 0 : options.scope) || this.scope;
270
+ const scopeValues = scope ? scope.split(' ') : [];
271
+ // Ensure the 'openid' scope is always included.
272
+ if (!scopeValues.includes('openid')) {
273
+ scopeValues.unshift('openid');
274
+ }
275
+ scope = scopeValues.join(' ');
276
+ const params = new URLSearchParams([
277
+ ['client_id', /** @type {string} */ (this.clientId)],
278
+ ['redirect_uri', redirectUri],
279
+ ['state', state],
280
+ ['response_mode', this.responseMode],
281
+ ['response_type', this.responseType],
282
+ ['scope', scope]
283
+ ]);
284
+ if (__classPrivateFieldGet(this, _TideCloak_useNonce, "f")) {
285
+ params.append('nonce', nonce);
286
+ }
287
+ if (options === null || options === void 0 ? void 0 : options.prompt) {
288
+ params.append('prompt', options.prompt);
289
+ }
290
+ if (typeof (options === null || options === void 0 ? void 0 : options.maxAge) === 'number') {
291
+ params.append('max_age', options.maxAge.toString());
292
+ }
293
+ if (options === null || options === void 0 ? void 0 : options.loginHint) {
294
+ params.append('login_hint', options.loginHint);
295
+ }
296
+ if (options === null || options === void 0 ? void 0 : options.idpHint) {
297
+ params.append('kc_idp_hint', options.idpHint);
298
+ }
299
+ if ((options === null || options === void 0 ? void 0 : options.action) && options.action !== 'register') {
300
+ params.append('kc_action', options.action);
301
+ }
302
+ if (options === null || options === void 0 ? void 0 : options.locale) {
303
+ params.append('ui_locales', options.locale);
304
+ }
305
+ if (options === null || options === void 0 ? void 0 : options.acr) {
306
+ params.append('claims', buildClaimsParameter(options.acr));
307
+ }
308
+ if (options === null || options === void 0 ? void 0 : options.acrValues) {
309
+ params.append('acr_values', options.acrValues);
310
+ }
311
+ if (this.pkceMethod) {
312
+ try {
313
+ const codeVerifier = generateCodeVerifier(96);
314
+ const pkceChallenge = await generatePkceChallenge(this.pkceMethod, codeVerifier);
315
+ callbackState.pkceCodeVerifier = codeVerifier;
316
+ params.append('code_challenge', pkceChallenge);
317
+ params.append('code_challenge_method', this.pkceMethod);
318
+ }
319
+ catch (error) {
320
+ throw new Error('Failed to generate PKCE challenge.', { cause: error });
321
+ }
322
+ }
323
+ __classPrivateFieldGet(this, _TideCloak_callbackStorage, "f").add(callbackState);
324
+ return `${url}?${params.toString()}`;
325
+ };
326
+ /**
327
+ * @param {TideCloakLogoutOptions} [options]
328
+ * @returns {Promise<void>}
329
+ */
330
+ this.logout = async (options) => {
331
+ var _a;
332
+ await ((_a = __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f")) === null || _a === void 0 ? void 0 : _a.flush());
333
+ return __classPrivateFieldGet(this, _TideCloak_adapter, "f").logout(options);
334
+ };
335
+ /**
336
+ * @param {TideCloakLogoutOptions} [options]
337
+ * @returns {string}
338
+ */
339
+ this.createLogoutUrl = (options) => {
340
+ var _a;
341
+ const logoutMethod = (_a = options === null || options === void 0 ? void 0 : options.logoutMethod) !== null && _a !== void 0 ? _a : this.logoutMethod;
342
+ const url = this.endpoints.logout();
343
+ if (logoutMethod === 'POST') {
344
+ return url;
345
+ }
346
+ const params = new URLSearchParams([
347
+ ['client_id', /** @type {string} */ (this.clientId)],
348
+ ['post_logout_redirect_uri', __classPrivateFieldGet(this, _TideCloak_adapter, "f").redirectUri(options)]
349
+ ]);
350
+ if (this.idToken) {
351
+ params.append('id_token_hint', this.idToken);
352
+ }
353
+ return `${url}?${params.toString()}`;
354
+ };
355
+ /**
356
+ * @param {TideCloakRegisterOptions} [options]
357
+ * @returns {Promise<void>}
358
+ */
359
+ this.register = (options) => {
360
+ return __classPrivateFieldGet(this, _TideCloak_adapter, "f").register(options);
361
+ };
362
+ /**
363
+ * @param {TideCloakRegisterOptions} [options]
364
+ * @returns {Promise<string>}
365
+ */
366
+ this.createRegisterUrl = (options) => {
367
+ return this.createLoginUrl({ ...options, action: 'register' });
368
+ };
369
+ /**
370
+ * @param {TideCloakAccountOptions} [options]
371
+ * @returns {string}
372
+ */
373
+ this.createAccountUrl = (options) => {
374
+ const url = __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_getRealmUrl).call(this);
375
+ if (!url) {
376
+ throw new Error('Unable to create account URL, make sure the adapter is not configured using a generic OIDC provider.');
377
+ }
378
+ const params = new URLSearchParams([
379
+ ['referrer', /** @type {string} */ (this.clientId)],
380
+ ['referrer_uri', __classPrivateFieldGet(this, _TideCloak_adapter, "f").redirectUri(options)]
381
+ ]);
382
+ return `${url}/account?${params.toString()}`;
383
+ };
384
+ /**
385
+ * @returns {Promise<void>}
386
+ */
387
+ this.accountManagement = () => {
388
+ return __classPrivateFieldGet(this, _TideCloak_adapter, "f").accountManagement();
389
+ };
390
+ /**
391
+ * @param {string} role
392
+ * @returns {boolean}
393
+ */
394
+ this.hasRealmRole = (role) => {
395
+ const access = this.realmAccess;
396
+ return !!access && access.roles.indexOf(role) >= 0;
397
+ };
398
+ /**
399
+ * @param {string} role
400
+ * @param {string} [resource]
401
+ * @returns {boolean}
402
+ */
403
+ this.hasResourceRole = (role, resource) => {
404
+ if (!this.resourceAccess) {
405
+ return false;
406
+ }
407
+ const access = this.resourceAccess[resource || /** @type {string} */ (this.clientId)];
408
+ return !!access && access.roles.indexOf(role) >= 0;
409
+ };
410
+ /**
411
+ * @returns {Promise<TideCloakProfile>}
412
+ */
413
+ this.loadUserProfile = async () => {
414
+ const realmUrl = __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_getRealmUrl).call(this);
415
+ if (!realmUrl) {
416
+ throw new Error('Unable to load user profile, make sure the adapter is not configured using a generic OIDC provider.');
417
+ }
418
+ const url = `${realmUrl}/account`;
419
+ /** @type {TideCloakProfile} */
420
+ const profile = await fetchJSON(url, {
421
+ headers: [buildAuthorizationHeader(this.token)]
422
+ });
423
+ return (this.profile = profile);
424
+ };
425
+ /**
426
+ * @returns {Promise<{}>}
427
+ */
428
+ this.loadUserInfo = async () => {
429
+ const url = this.endpoints.userinfo();
430
+ /** @type {{}} */
431
+ const userInfo = await fetchJSON(url, {
432
+ headers: [buildAuthorizationHeader(this.token)]
433
+ });
434
+ return (this.userInfo = userInfo);
435
+ };
436
+ /**
437
+ * @param {number} [minValidity]
438
+ * @returns {boolean}
439
+ */
440
+ this.isTokenExpired = (minValidity) => {
441
+ if (!this.tokenParsed || (!this.refreshToken && this.flow !== 'implicit')) {
442
+ throw new Error('Not authenticated');
443
+ }
444
+ if (this.timeSkew == null) {
445
+ __classPrivateFieldGet(this, _TideCloak_logInfo, "f").call(this, '[TIDECLOAK] Unable to determine if token is expired as timeskew is not set');
446
+ return true;
447
+ }
448
+ if (typeof this.tokenParsed.exp !== 'number') {
449
+ return false;
450
+ }
451
+ let expiresIn = this.tokenParsed.exp - Math.ceil(new Date().getTime() / 1000) + this.timeSkew;
452
+ if (minValidity) {
453
+ if (isNaN(minValidity)) {
454
+ throw new Error('Invalid minValidity');
455
+ }
456
+ expiresIn -= minValidity;
457
+ }
458
+ return expiresIn < 0;
459
+ };
460
+ /**
461
+ * @param {number} minValidity
462
+ * @returns {Promise<boolean>}
463
+ */
464
+ this.updateToken = async (minValidity) => {
465
+ var _a, _b;
466
+ if (!this.refreshToken) {
467
+ throw new Error('Unable to update token, no refresh token available.');
468
+ }
469
+ minValidity = minValidity || 5;
470
+ if (__classPrivateFieldGet(this, _TideCloak_loginIframe, "f").enable) {
471
+ await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_checkLoginIframe).call(this);
472
+ }
473
+ let refreshToken = false;
474
+ if (minValidity === -1) {
475
+ refreshToken = true;
476
+ __classPrivateFieldGet(this, _TideCloak_logInfo, "f").call(this, '[TIDECLOAK] Refreshing token: forced refresh');
477
+ }
478
+ else if (!this.tokenParsed || this.isTokenExpired(minValidity)) {
479
+ refreshToken = true;
480
+ __classPrivateFieldGet(this, _TideCloak_logInfo, "f").call(this, '[TIDECLOAK] Refreshing token: token expired');
481
+ }
482
+ if (!refreshToken) {
483
+ return false;
484
+ }
485
+ /** @type {PromiseWithResolvers<boolean>} */
486
+ const { promise, resolve, reject } = Promise.withResolvers();
487
+ __classPrivateFieldGet(this, _TideCloak_refreshQueue, "f").push({ resolve, reject });
488
+ if (__classPrivateFieldGet(this, _TideCloak_refreshQueue, "f").length === 1) {
489
+ const url = this.endpoints.token();
490
+ let timeLocal = new Date().getTime();
491
+ try {
492
+ const response = await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_fetchRefreshToken).call(this, url, this.refreshToken, /** @type {string} */ (this.clientId));
493
+ __classPrivateFieldGet(this, _TideCloak_logInfo, "f").call(this, '[TIDECLOAK] Token refreshed');
494
+ timeLocal = (timeLocal + new Date().getTime()) / 2;
495
+ __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_setToken).call(this, response.access_token, response.refresh_token, response.id_token, timeLocal, response.doken);
496
+ (_a = this.onAuthRefreshSuccess) === null || _a === void 0 ? void 0 : _a.call(this);
497
+ for (let p = __classPrivateFieldGet(this, _TideCloak_refreshQueue, "f").pop(); p != null; p = __classPrivateFieldGet(this, _TideCloak_refreshQueue, "f").pop()) {
498
+ p.resolve(true);
499
+ }
500
+ }
501
+ catch (error) {
502
+ __classPrivateFieldGet(this, _TideCloak_logWarn, "f").call(this, '[TIDECLOAK] Failed to refresh token');
503
+ if (error instanceof NetworkError && error.response.status === 400) {
504
+ this.clearToken();
505
+ }
506
+ (_b = this.onAuthRefreshError) === null || _b === void 0 ? void 0 : _b.call(this);
507
+ for (let p = __classPrivateFieldGet(this, _TideCloak_refreshQueue, "f").pop(); p != null; p = __classPrivateFieldGet(this, _TideCloak_refreshQueue, "f").pop()) {
508
+ p.reject(error);
509
+ }
510
+ }
511
+ }
512
+ return await promise;
513
+ };
514
+ this.clearToken = () => {
515
+ var _a;
516
+ if (this.token) {
517
+ __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_setToken).call(this);
518
+ (_a = this.onAuthLogout) === null || _a === void 0 ? void 0 : _a.call(this);
519
+ if (this.loginRequired) {
520
+ this.login();
521
+ }
522
+ }
523
+ };
524
+ this.secureFetch = async (url, init = {}) => {
525
+ var _a, _b;
526
+ const dpopProvider = __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f");
527
+ if (dpopProvider && this.authenticated && this.token) {
528
+ const existingAuth = new Headers(init.headers).get('Authorization');
529
+ const isOurBearerToken = existingAuth === `Bearer ${this.token}`;
530
+ if (!isOurBearerToken) {
531
+ // Quick escape - didn't put this check in first if statement as it's more expensive than other checks
532
+ return fetch(url, init);
533
+ }
534
+ const urlString = url instanceof URL ? url.href : url.toString();
535
+ const origin = new URL(urlString).origin;
536
+ const method = (_a = init.method) !== null && _a !== void 0 ? _a : 'GET';
537
+ const resourceNonce = dpopProvider.getResourceServerNonce(origin);
538
+ const proof = await dpopProvider.generateDPoPProof(urlString, method, this.token, resourceNonce);
539
+ const headers = new Headers(init.headers);
540
+ headers.set('Authorization', `DPoP ${this.token}`);
541
+ headers.set('DPoP', proof);
542
+ const resp = await fetch(url, { ...init, headers });
543
+ // Check for new nonce in response
544
+ const newNonce = resp.headers.get('DPoP-Nonce');
545
+ if (newNonce) {
546
+ dpopProvider.updateResourceServerNonce(origin, newNonce);
547
+ }
548
+ // https://datatracker.ietf.org/doc/html/rfc9449#section-9
549
+ // Resource servers signal via: WWW-Authenticate: DPoP error="use_dpop_nonce"
550
+ if (resp.status === 401 && newNonce) {
551
+ const wwwAuth = (_b = resp.headers.get('WWW-Authenticate')) !== null && _b !== void 0 ? _b : '';
552
+ if (wwwAuth.includes('DPoP') && wwwAuth.includes('error="use_dpop_nonce"')) {
553
+ const retryProof = await dpopProvider.generateDPoPProof(urlString, method, this.token, newNonce);
554
+ const retryHeaders = new Headers(init.headers);
555
+ retryHeaders.set('Authorization', `DPoP ${this.token}`);
556
+ retryHeaders.set('DPoP', retryProof);
557
+ const retryResp = await fetch(url, { ...init, headers: retryHeaders });
558
+ // Capture nonce from retry response for future requests
559
+ const retryNonce = retryResp.headers.get('DPoP-Nonce');
560
+ if (retryNonce) {
561
+ dpopProvider.updateResourceServerNonce(origin, retryNonce);
562
+ }
563
+ return retryResp;
564
+ }
565
+ }
566
+ return resp;
567
+ }
568
+ else {
569
+ return fetch(url, init);
570
+ }
571
+ };
108
572
  if (typeof config !== 'string' && !isObject(config)) {
109
573
  throw new Error("The 'TideCloak' constructor must be provided with a configuration object, or a URL to a JSON configuration file.");
110
574
  }
111
- // if (isObject(config)) {
112
- // const requiredProperties = 'oidcProvider' in config
113
- // ? ['clientId']
114
- // : ['url', 'realm', 'clientId', 'homeOrkUrl', 'vendorId', 'clientOriginAuth']
115
- // for (const property of requiredProperties) {
116
- // if (!config[property]) {
117
- // throw new Error(`The configuration object is missing the required '${property}' property.`)
118
- // }
119
- // }
120
- // }
575
+ if (isObject(config)) {
576
+ const requiredProperties = 'oidcProvider' in config
577
+ ? ['clientId']
578
+ : ['url', 'realm', 'clientId'];
579
+ for (const property of requiredProperties) {
580
+ if (!(property in config)) {
581
+ throw new Error(`The configuration object is missing the required '${property}' property.`);
582
+ }
583
+ }
584
+ }
121
585
  if (!globalThis.isSecureContext) {
122
586
  __classPrivateFieldGet(this, _TideCloak_logWarn, "f").call(this, "[TIDECLOAK] TideCloak JS must be used in a 'secure context' to function properly as it relies on browser APIs that are otherwise not available.\n" +
123
587
  'Continuing to run your application insecurely will lead to unexpected behavior and breakage.\n\n' +
@@ -125,113 +589,7 @@ class TideCloak {
125
589
  }
126
590
  __classPrivateFieldSet(this, _TideCloak_config, config, "f");
127
591
  }
128
- /**
129
- * @param {KeycloakInitOptions} initOptions
130
- * @returns {Promise<boolean>}
131
- */
132
- async init(initOptions = {}) {
133
- var _a;
134
- if (this.didInitialize) {
135
- throw new Error("A 'TideCloak' instance can only be initialized once.");
136
- }
137
- this.didInitialize = true;
138
- __classPrivateFieldSet(this, _TideCloak_callbackStorage, createCallbackStorage(), "f");
139
- const adapters = ['default', 'cordova', 'cordova-native'];
140
- if (typeof initOptions.adapter === 'string' && adapters.includes(initOptions.adapter)) {
141
- __classPrivateFieldSet(this, _TideCloak_adapter, __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_loadAdapter).call(this, initOptions.adapter), "f");
142
- }
143
- else if (typeof initOptions.adapter === 'object') {
144
- __classPrivateFieldSet(this, _TideCloak_adapter, initOptions.adapter, "f");
145
- }
146
- else if ('Cordova' in window || 'cordova' in window) {
147
- __classPrivateFieldSet(this, _TideCloak_adapter, __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_loadAdapter).call(this, 'cordova'), "f");
148
- }
149
- else {
150
- __classPrivateFieldSet(this, _TideCloak_adapter, __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_loadAdapter).call(this, 'default'), "f");
151
- }
152
- if (typeof initOptions.useNonce !== 'undefined') {
153
- __classPrivateFieldSet(this, _TideCloak_useNonce, initOptions.useNonce, "f");
154
- }
155
- if (typeof initOptions.checkLoginIframe !== 'undefined') {
156
- __classPrivateFieldGet(this, _TideCloak_loginIframe, "f").enable = initOptions.checkLoginIframe;
157
- }
158
- if (initOptions.checkLoginIframeInterval) {
159
- __classPrivateFieldGet(this, _TideCloak_loginIframe, "f").interval = initOptions.checkLoginIframeInterval;
160
- }
161
- if (initOptions.onLoad === 'login-required') {
162
- this.loginRequired = true;
163
- }
164
- if (initOptions.responseMode) {
165
- if (initOptions.responseMode === 'query' || initOptions.responseMode === 'fragment') {
166
- this.responseMode = initOptions.responseMode;
167
- }
168
- else {
169
- throw new Error('Invalid value for responseMode');
170
- }
171
- }
172
- if (initOptions.flow) {
173
- switch (initOptions.flow) {
174
- case 'standard':
175
- this.responseType = 'code';
176
- break;
177
- case 'implicit':
178
- this.responseType = 'id_token token';
179
- break;
180
- case 'hybrid':
181
- this.responseType = 'code id_token token';
182
- break;
183
- default:
184
- throw new Error('Invalid value for flow');
185
- }
186
- this.flow = initOptions.flow;
187
- }
188
- if (typeof initOptions.timeSkew === 'number') {
189
- this.timeSkew = initOptions.timeSkew;
190
- }
191
- if (initOptions.redirectUri) {
192
- this.redirectUri = initOptions.redirectUri;
193
- }
194
- if (initOptions.silentCheckSsoRedirectUri) {
195
- this.silentCheckSsoRedirectUri = initOptions.silentCheckSsoRedirectUri;
196
- }
197
- if (typeof initOptions.silentCheckSsoFallback === 'boolean') {
198
- this.silentCheckSsoFallback = initOptions.silentCheckSsoFallback;
199
- }
200
- if (typeof initOptions.pkceMethod !== 'undefined') {
201
- if (initOptions.pkceMethod !== 'S256' && initOptions.pkceMethod !== false) {
202
- throw new TypeError(`Invalid value for pkceMethod', expected 'S256' or false but got ${initOptions.pkceMethod}.`);
203
- }
204
- this.pkceMethod = initOptions.pkceMethod;
205
- }
206
- if (typeof initOptions.enableLogging === 'boolean') {
207
- this.enableLogging = initOptions.enableLogging;
208
- }
209
- if (initOptions.logoutMethod === 'POST') {
210
- this.logoutMethod = 'POST';
211
- }
212
- if (typeof initOptions.scope === 'string') {
213
- this.scope = initOptions.scope;
214
- }
215
- if (typeof initOptions.acrValues === 'string') {
216
- this.acrValues = initOptions.acrValues;
217
- }
218
- if (typeof initOptions.messageReceiveTimeout === 'number' && initOptions.messageReceiveTimeout > 0) {
219
- this.messageReceiveTimeout = initOptions.messageReceiveTimeout;
220
- }
221
- await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_loadConfig).call(this);
222
- await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_check3pCookiesSupported).call(this);
223
- await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_processInit).call(this, initOptions);
224
- (_a = this.onReady) === null || _a === void 0 ? void 0 : _a.call(this, this.authenticated);
225
- return this.authenticated;
226
- }
227
592
  ;
228
- /**
229
- * @param {KeycloakLoginOptions} [options]
230
- * @returns {Promise<void>}
231
- */
232
- login(options) {
233
- return __classPrivateFieldGet(this, _TideCloak_adapter, "f").login(options);
234
- }
235
593
  /**
236
594
  * Ensure the access token is valid, refreshing if needed.
237
595
  * @returns {Promise<void>}
@@ -243,283 +601,6 @@ class TideCloak {
243
601
  await this.updateToken(-1);
244
602
  }
245
603
  }
246
- /**
247
- * @param {KeycloakLoginOptions} [options]
248
- * @returns {Promise<string>}
249
- */
250
- async createLoginUrl(options) {
251
- const state = createUUID();
252
- const nonce = createUUID();
253
- const redirectUri = __classPrivateFieldGet(this, _TideCloak_adapter, "f").redirectUri(options);
254
- /** @type {CallbackState} */
255
- const callbackState = {
256
- state,
257
- nonce,
258
- redirectUri,
259
- loginOptions: options
260
- };
261
- if (options === null || options === void 0 ? void 0 : options.prompt) {
262
- callbackState.prompt = options.prompt;
263
- }
264
- const url = (options === null || options === void 0 ? void 0 : options.action) === 'register'
265
- ? this.endpoints.register()
266
- : this.endpoints.authorize();
267
- let scope = (options === null || options === void 0 ? void 0 : options.scope) || this.scope;
268
- const scopeValues = scope ? scope.split(' ') : [];
269
- // Ensure the 'openid' scope is always included.
270
- if (!scopeValues.includes('openid')) {
271
- scopeValues.unshift('openid');
272
- }
273
- scope = scopeValues.join(' ');
274
- const params = new URLSearchParams([
275
- ['client_id', /** @type {string} */ (this.clientId)],
276
- // The endpoint URI MUST NOT include a fragment component.
277
- // https://datatracker.ietf.org/doc/html/rfc6749#section-3.1.2
278
- ['redirect_uri', stripHash(redirectUri)],
279
- ['state', state],
280
- ['response_mode', this.responseMode],
281
- ['response_type', this.responseType],
282
- ['scope', scope]
283
- ]);
284
- if (__classPrivateFieldGet(this, _TideCloak_useNonce, "f")) {
285
- params.append('nonce', nonce);
286
- }
287
- if (options === null || options === void 0 ? void 0 : options.prompt) {
288
- params.append('prompt', options.prompt);
289
- }
290
- if (typeof (options === null || options === void 0 ? void 0 : options.maxAge) === 'number') {
291
- params.append('max_age', options.maxAge.toString());
292
- }
293
- if (options === null || options === void 0 ? void 0 : options.loginHint) {
294
- params.append('login_hint', options.loginHint);
295
- }
296
- if (options === null || options === void 0 ? void 0 : options.idpHint) {
297
- params.append('kc_idp_hint', options.idpHint);
298
- }
299
- if ((options === null || options === void 0 ? void 0 : options.action) && options.action !== 'register') {
300
- params.append('kc_action', options.action);
301
- }
302
- if (options === null || options === void 0 ? void 0 : options.locale) {
303
- params.append('ui_locales', options.locale);
304
- }
305
- if (options === null || options === void 0 ? void 0 : options.acr) {
306
- params.append('claims', buildClaimsParameter(options.acr));
307
- }
308
- if ((options === null || options === void 0 ? void 0 : options.acrValues) || this.acrValues) {
309
- params.append('acr_values', options.acrValues || this.acrValues);
310
- }
311
- if (this.pkceMethod) {
312
- try {
313
- const codeVerifier = generateCodeVerifier(96);
314
- const pkceChallenge = await generatePkceChallenge(this.pkceMethod, codeVerifier);
315
- callbackState.pkceCodeVerifier = codeVerifier;
316
- params.append('code_challenge', pkceChallenge);
317
- params.append('code_challenge_method', this.pkceMethod);
318
- }
319
- catch (error) {
320
- throw new Error('Failed to generate PKCE challenge.', { cause: error });
321
- }
322
- }
323
- __classPrivateFieldGet(this, _TideCloak_callbackStorage, "f").add(callbackState);
324
- return `${url}?${params.toString()}`;
325
- }
326
- /**
327
- * @param {KeycloakLogoutOptions} [options]
328
- * @returns {Promise<void>}
329
- */
330
- logout(options) {
331
- return __classPrivateFieldGet(this, _TideCloak_adapter, "f").logout(options);
332
- }
333
- /**
334
- * @param {KeycloakLogoutOptions} [options]
335
- * @returns {string}
336
- */
337
- createLogoutUrl(options) {
338
- var _a;
339
- const logoutMethod = (_a = options === null || options === void 0 ? void 0 : options.logoutMethod) !== null && _a !== void 0 ? _a : this.logoutMethod;
340
- const url = this.endpoints.logout();
341
- if (logoutMethod === 'POST') {
342
- return url;
343
- }
344
- const params = new URLSearchParams([
345
- ['client_id', /** @type {string} */ (this.clientId)],
346
- ['post_logout_redirect_uri', __classPrivateFieldGet(this, _TideCloak_adapter, "f").redirectUri(options)]
347
- ]);
348
- if (this.idToken) {
349
- params.append('id_token_hint', this.idToken);
350
- }
351
- return `${url}?${params.toString()}`;
352
- }
353
- /**
354
- * @param {KeycloakRegisterOptions} [options]
355
- * @returns {Promise<void>}
356
- */
357
- register(options) {
358
- return __classPrivateFieldGet(this, _TideCloak_adapter, "f").register(options);
359
- }
360
- /**
361
- * @param {KeycloakRegisterOptions} [options]
362
- * @returns {Promise<string>}
363
- */
364
- createRegisterUrl(options) {
365
- return this.createLoginUrl({ ...options, action: 'register' });
366
- }
367
- /**
368
- * @param {KeycloakAccountOptions} [options]
369
- * @returns {string}
370
- */
371
- createAccountUrl(options) {
372
- const url = __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_getRealmUrl).call(this);
373
- if (!url) {
374
- throw new Error('Unable to create account URL, make sure the adapter is not configured using a generic OIDC provider.');
375
- }
376
- const params = new URLSearchParams([
377
- ['referrer', /** @type {string} */ (this.clientId)],
378
- ['referrer_uri', __classPrivateFieldGet(this, _TideCloak_adapter, "f").redirectUri(options)]
379
- ]);
380
- return `${url}/account?${params.toString()}`;
381
- }
382
- /**
383
- * @returns {Promise<void>}
384
- */
385
- accountManagement() {
386
- return __classPrivateFieldGet(this, _TideCloak_adapter, "f").accountManagement();
387
- }
388
- /**
389
- * @param {string} role
390
- * @returns {boolean}
391
- */
392
- hasRealmRole(role) {
393
- const access = this.realmAccess;
394
- return !!access && access.roles.indexOf(role) >= 0;
395
- }
396
- /**
397
- * @param {string} role
398
- * @param {string} [resource]
399
- * @returns {boolean}
400
- */
401
- hasResourceRole(role, resource) {
402
- if (!this.resourceAccess) {
403
- return false;
404
- }
405
- const access = this.resourceAccess[resource || /** @type {string} */ (this.clientId)];
406
- return !!access && access.roles.indexOf(role) >= 0;
407
- }
408
- /**
409
- * @returns {Promise<KeycloakProfile>}
410
- */
411
- async loadUserProfile() {
412
- const realmUrl = __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_getRealmUrl).call(this);
413
- if (!realmUrl) {
414
- throw new Error('Unable to load user profile, make sure the adapter is not configured using a generic OIDC provider.');
415
- }
416
- const url = `${realmUrl}/account`;
417
- /** @type {KeycloakProfile} */
418
- const profile = await fetchJSON(url, {
419
- headers: [buildAuthorizationHeader(this.token)]
420
- });
421
- return (this.profile = profile);
422
- }
423
- /**
424
- * @returns {Promise<KeycloakUserInfo>}
425
- */
426
- async loadUserInfo() {
427
- const url = this.endpoints.userinfo();
428
- /** @type {KeycloakUserInfo} */
429
- const userInfo = await fetchJSON(url, {
430
- headers: [buildAuthorizationHeader(this.token)]
431
- });
432
- return (this.userInfo = userInfo);
433
- }
434
- /**
435
- * @param {number} [minValidity]
436
- * @returns {boolean}
437
- */
438
- isTokenExpired(minValidity) {
439
- if (!this.tokenParsed || (!this.refreshToken && this.flow !== 'implicit')) {
440
- throw new Error('Not authenticated');
441
- }
442
- if (this.timeSkew == null) {
443
- __classPrivateFieldGet(this, _TideCloak_logInfo, "f").call(this, '[TIDECLOAK] Unable to determine if token is expired as timeskew is not set');
444
- return true;
445
- }
446
- if (typeof this.tokenParsed.exp !== 'number') {
447
- return false;
448
- }
449
- let expiresIn = this.tokenParsed.exp - Math.ceil(new Date().getTime() / 1000) + this.timeSkew;
450
- if (minValidity) {
451
- if (isNaN(minValidity)) {
452
- throw new Error('Invalid minValidity');
453
- }
454
- expiresIn -= minValidity;
455
- }
456
- return expiresIn < 0;
457
- }
458
- /**
459
- * Matches Keycloak: minValidity is optional.
460
- * @param {number} [minValidity]
461
- * @returns {Promise<boolean>}
462
- */
463
- async updateToken(minValidity) {
464
- var _a, _b;
465
- if (!this.refreshToken) {
466
- throw new Error('Unable to update token, no refresh token available.');
467
- }
468
- minValidity = minValidity || 5;
469
- if (__classPrivateFieldGet(this, _TideCloak_loginIframe, "f").enable) {
470
- await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_checkLoginIframe).call(this);
471
- }
472
- let refreshToken = false;
473
- if (minValidity === -1) {
474
- refreshToken = true;
475
- __classPrivateFieldGet(this, _TideCloak_logInfo, "f").call(this, '[TIDECLOAK] Refreshing token: forced refresh');
476
- }
477
- else if (!this.tokenParsed || this.isTokenExpired(minValidity)) {
478
- refreshToken = true;
479
- __classPrivateFieldGet(this, _TideCloak_logInfo, "f").call(this, '[TIDECLOAK] Refreshing token: token expired');
480
- }
481
- if (!refreshToken) {
482
- return false;
483
- }
484
- /** @type {PromiseWithResolvers<boolean>} */
485
- const { promise, resolve, reject } = Promise.withResolvers();
486
- __classPrivateFieldGet(this, _TideCloak_refreshQueue, "f").push({ resolve, reject });
487
- if (__classPrivateFieldGet(this, _TideCloak_refreshQueue, "f").length === 1) {
488
- const url = this.endpoints.token();
489
- let timeLocal = new Date().getTime();
490
- try {
491
- const response = await fetchRefreshToken(url, this.refreshToken, /** @type {string} */ (this.clientId));
492
- __classPrivateFieldGet(this, _TideCloak_logInfo, "f").call(this, '[TIDECLOAK] Token refreshed');
493
- timeLocal = (timeLocal + new Date().getTime()) / 2;
494
- __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_setToken).call(this, response.access_token, response.refresh_token, response.id_token, timeLocal, response.doken);
495
- (_a = this.onAuthRefreshSuccess) === null || _a === void 0 ? void 0 : _a.call(this);
496
- for (let p = __classPrivateFieldGet(this, _TideCloak_refreshQueue, "f").pop(); p != null; p = __classPrivateFieldGet(this, _TideCloak_refreshQueue, "f").pop()) {
497
- p.resolve(true);
498
- }
499
- }
500
- catch (error) {
501
- __classPrivateFieldGet(this, _TideCloak_logWarn, "f").call(this, '[TIDECLOAK] Failed to refresh token');
502
- if (error instanceof NetworkError && error.response.status === 400) {
503
- this.clearToken();
504
- }
505
- (_b = this.onAuthRefreshError) === null || _b === void 0 ? void 0 : _b.call(this);
506
- for (let p = __classPrivateFieldGet(this, _TideCloak_refreshQueue, "f").pop(); p != null; p = __classPrivateFieldGet(this, _TideCloak_refreshQueue, "f").pop()) {
507
- p.reject(error);
508
- }
509
- }
510
- }
511
- return await promise;
512
- }
513
- clearToken() {
514
- var _a;
515
- if (this.token) {
516
- __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_setToken).call(this);
517
- (_a = this.onAuthLogout) === null || _a === void 0 ? void 0 : _a.call(this);
518
- if (this.loginRequired) {
519
- this.login();
520
- }
521
- }
522
- }
523
604
  /**
524
605
  * Initialize Tide RequestEnclave.
525
606
  */
@@ -703,7 +784,7 @@ class TideCloak {
703
784
  return decrypted.map((d, i) => (dataToSend[i].isRaw ? d : StringFromUint8Array(d)));
704
785
  }
705
786
  }
706
- _TideCloak_refreshQueue = new WeakMap(), _TideCloak_adapter = new WeakMap(), _TideCloak_useNonce = new WeakMap(), _TideCloak_callbackStorage = new WeakMap(), _TideCloak_logInfo = new WeakMap(), _TideCloak_logWarn = new WeakMap(), _TideCloak_loginIframe = new WeakMap(), _TideCloak_config = new WeakMap(), _TideCloak_instances = new WeakSet(), _TideCloak_loadAdapter = function _TideCloak_loadAdapter(type) {
787
+ _TideCloak_refreshQueue = new WeakMap(), _TideCloak_adapter = new WeakMap(), _TideCloak_useNonce = new WeakMap(), _TideCloak_callbackStorage = new WeakMap(), _TideCloak_logInfo = new WeakMap(), _TideCloak_logWarn = new WeakMap(), _TideCloak_loginIframe = new WeakMap(), _TideCloak_dpopProvider = new WeakMap(), _TideCloak_config = new WeakMap(), _TideCloak_instances = new WeakSet(), _TideCloak_loadAdapter = function _TideCloak_loadAdapter(type) {
707
788
  if (type === 'default') {
708
789
  return __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_loadDefaultAdapter).call(this);
709
790
  }
@@ -717,7 +798,7 @@ _TideCloak_refreshQueue = new WeakMap(), _TideCloak_adapter = new WeakMap(), _Ti
717
798
  }
718
799
  throw new Error('invalid adapter type: ' + type);
719
800
  }, _TideCloak_loadDefaultAdapter = function _TideCloak_loadDefaultAdapter() {
720
- /** @type {KeycloakAdapter['redirectUri']}{} */
801
+ /** @type {TideCloakAdapter['redirectUri']}{} */
721
802
  const redirectUri = (options) => {
722
803
  return (options === null || options === void 0 ? void 0 : options.redirectUri) || this.redirectUri || globalThis.location.href;
723
804
  };
@@ -1021,7 +1102,7 @@ async function _TideCloak_loadConfig() {
1021
1102
  this.authServerUrl = jsonConfig['auth-server-url'];
1022
1103
  this.realm = jsonConfig.realm;
1023
1104
  this.clientId = jsonConfig.resource;
1024
- __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_setupEndpoints).call(this);
1105
+ await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_setupEndpoints).call(this);
1025
1106
  }
1026
1107
  else {
1027
1108
  this.clientId = __classPrivateFieldGet(this, _TideCloak_config, "f").clientId;
@@ -1031,10 +1112,31 @@ async function _TideCloak_loadConfig() {
1031
1112
  else {
1032
1113
  this.authServerUrl = __classPrivateFieldGet(this, _TideCloak_config, "f").url;
1033
1114
  this.realm = __classPrivateFieldGet(this, _TideCloak_config, "f").realm;
1034
- __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_setupEndpoints).call(this);
1115
+ await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_setupEndpoints).call(this);
1116
+ }
1117
+ }
1118
+ }, _TideCloak_setupEndpoints =
1119
+ /**
1120
+ * @returns {Promise<void>}
1121
+ */
1122
+ async function _TideCloak_setupEndpoints() {
1123
+ // Fetch OIDC metadata to get DPoP supported algorithms (only if DPoP is enabled)
1124
+ if (this.useDPoP) {
1125
+ const issuerUrl = __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_getIssuerUrl).call(this);
1126
+ if (!issuerUrl) {
1127
+ __classPrivateFieldGet(this, _TideCloak_logWarn, "f").call(this, '[TIDECLOAK] Cannot fetch OIDC metadata: issuer URL is not available');
1128
+ }
1129
+ else {
1130
+ const url = `${stripTrailingSlash(issuerUrl)}/.well-known/openid-configuration`;
1131
+ try {
1132
+ const openIdConfig = await fetchOpenIdConfig(url);
1133
+ this.dpopSigningAlgValuesSupported = openIdConfig.dpop_signing_alg_values_supported ? openIdConfig.dpop_signing_alg_values_supported : undefined;
1134
+ }
1135
+ catch (error) {
1136
+ __classPrivateFieldGet(this, _TideCloak_logWarn, "f").call(this, '[TIDECLOAK] Failed to fetch OIDC metadata for DPoP configuration: ' + (error instanceof Error ? error.message : error));
1137
+ }
1035
1138
  }
1036
1139
  }
1037
- }, _TideCloak_setupEndpoints = function _TideCloak_setupEndpoints() {
1038
1140
  this.endpoints = {
1039
1141
  authorize: () => {
1040
1142
  return __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_getRealmUrl).call(this) + '/protocol/openid-connect/auth';
@@ -1073,6 +1175,9 @@ async function _TideCloak_loadOidcConfig(oidcProvider) {
1073
1175
  __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_setupOidcEndpoints).call(this, oidcProvider);
1074
1176
  }
1075
1177
  }, _TideCloak_setupOidcEndpoints = function _TideCloak_setupOidcEndpoints(config) {
1178
+ // Store DPoP supported algorithms if present
1179
+ this.dpopSigningAlgValuesSupported = config.dpop_signing_alg_values_supported;
1180
+ this.issuer = config.issuer;
1076
1181
  this.endpoints = {
1077
1182
  authorize() {
1078
1183
  return config.authorization_endpoint;
@@ -1102,6 +1207,11 @@ async function _TideCloak_loadOidcConfig(oidcProvider) {
1102
1207
  return config.userinfo_endpoint;
1103
1208
  }
1104
1209
  };
1210
+ }, _TideCloak_getIssuerUrl = function _TideCloak_getIssuerUrl() {
1211
+ if (this.issuer)
1212
+ return this.issuer;
1213
+ const realmUrl = __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_getRealmUrl).call(this);
1214
+ return realmUrl || undefined;
1105
1215
  }, _TideCloak_check3pCookiesSupported =
1106
1216
  /**
1107
1217
  * @returns {Promise<void>}
@@ -1147,14 +1257,14 @@ async function _TideCloak_check3pCookiesSupported() {
1147
1257
  return await applyTimeoutToPromise(promise, this.messageReceiveTimeout, 'Timeout when waiting for 3rd party check iframe message.');
1148
1258
  }, _TideCloak_processInit =
1149
1259
  /**
1150
- * @param {KeycloakInitOptions} initOptions
1260
+ * @param {TideCloakInitOptions} initOptions
1151
1261
  * @returns {Promise<void>}
1152
1262
  */
1153
1263
  async function _TideCloak_processInit(initOptions) {
1154
1264
  var _a, _b, _c;
1155
1265
  const callback = __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_parseCallback).call(this, window.location.href);
1156
- if (callback === null || callback === void 0 ? void 0 : callback.redirectUri) {
1157
- window.history.replaceState(window.history.state, '', callback.redirectUri);
1266
+ if (callback === null || callback === void 0 ? void 0 : callback.newUrl) {
1267
+ window.history.replaceState(window.history.state, '', callback.newUrl);
1158
1268
  }
1159
1269
  if (callback && callback.valid) {
1160
1270
  await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_setupCheckLoginIframe).call(this);
@@ -1163,7 +1273,7 @@ async function _TideCloak_processInit(initOptions) {
1163
1273
  }
1164
1274
  /** @param {boolean} prompt */
1165
1275
  const doLogin = async (prompt) => {
1166
- /** @type {KeycloakLoginOptions} */
1276
+ /** @type {TideCloakLoginOptions} */
1167
1277
  const options = {};
1168
1278
  if (!prompt) {
1169
1279
  options.prompt = 'none';
@@ -1364,28 +1474,28 @@ async function _TideCloak_checkSsoSilently() {
1364
1474
  supportedParams.push('error_description');
1365
1475
  supportedParams.push('error_uri');
1366
1476
  const url = new URL(urlString);
1367
- let redirectUri = '';
1477
+ let newUrl = '';
1368
1478
  let parsed;
1369
1479
  if (this.responseMode === 'query' && url.searchParams.size > 0) {
1370
1480
  parsed = __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_parseCallbackParams).call(this, url.search, supportedParams);
1371
1481
  url.search = parsed.paramsString;
1372
- redirectUri = url.toString();
1482
+ newUrl = url.toString();
1373
1483
  }
1374
1484
  else if (this.responseMode === 'fragment' && url.hash.length > 0) {
1375
1485
  parsed = __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_parseCallbackParams).call(this, url.hash.substring(1), supportedParams);
1376
- url.hash = '';
1377
- redirectUri = url.toString();
1486
+ url.hash = parsed.paramsString;
1487
+ newUrl = url.toString();
1378
1488
  }
1379
1489
  if (parsed === null || parsed === void 0 ? void 0 : parsed.oauthParams) {
1380
1490
  if (this.flow === 'standard' || this.flow === 'hybrid') {
1381
1491
  if ((parsed.oauthParams.code || parsed.oauthParams.error) && parsed.oauthParams.state) {
1382
- parsed.oauthParams.redirectUri = redirectUri;
1492
+ parsed.oauthParams.newUrl = newUrl;
1383
1493
  return parsed.oauthParams;
1384
1494
  }
1385
1495
  }
1386
1496
  else if (this.flow === 'implicit') {
1387
1497
  if ((parsed.oauthParams.access_token || parsed.oauthParams.error) && parsed.oauthParams.state) {
1388
- parsed.oauthParams.redirectUri = redirectUri;
1498
+ parsed.oauthParams.newUrl = newUrl;
1389
1499
  return parsed.oauthParams;
1390
1500
  }
1391
1501
  }
@@ -1445,7 +1555,7 @@ async function _TideCloak_checkSsoSilently() {
1445
1555
  }
1446
1556
  if ((this.flow !== 'implicit') && code) {
1447
1557
  try {
1448
- const response = await fetchAccessToken(this.endpoints.token(), code, /** @type {string} */ (this.clientId), oauth.redirectUri, oauth.pkceCodeVerifier);
1558
+ const response = await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_fetchAccessToken).call(this, this.endpoints.token(), code, /** @type {string} */ (this.clientId), oauth.redirectUri, oauth.pkceCodeVerifier);
1449
1559
  authSuccess(response.access_token, response.refresh_token, response.id_token, response.doken);
1450
1560
  if (this.flow === 'standard') {
1451
1561
  (_c = this.onAuthSuccess) === null || _c === void 0 ? void 0 : _c.call(this);
@@ -1465,6 +1575,116 @@ async function _TideCloak_checkSsoSilently() {
1465
1575
  await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_scheduleCheckIframe).call(this);
1466
1576
  }
1467
1577
  }
1578
+ }, _TideCloak_fetchAccessToken =
1579
+ /**
1580
+ * @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}.
1581
+ * @property {string} access_token The access token issued by the authorization server.
1582
+ * @property {string} token_type The type of the token issued by the authorization server.
1583
+ * @property {number} [expires_in] The lifetime in seconds of the access token.
1584
+ * @property {string} [refresh_token] The refresh token issued by the authorization server.
1585
+ * @property {string} [id_token] The ID token issued by the authorization server, if requested.
1586
+ * @property {string} [scope] The scope of the access token.
1587
+ * @property {string} [doken] The Tide doken issued by the authorization server.
1588
+ */
1589
+ /**
1590
+ * Fetch the access token from the given URL.
1591
+ * @param {string} url
1592
+ * @param {string} code
1593
+ * @param {string} clientId
1594
+ * @param {string} redirectUri
1595
+ * @param {string} [pkceCodeVerifier]
1596
+ * @returns {Promise<AccessTokenResponse>}
1597
+ */
1598
+ async function _TideCloak_fetchAccessToken(url, code, clientId, redirectUri, pkceCodeVerifier) {
1599
+ const body = new URLSearchParams([
1600
+ ['code', code],
1601
+ ['grant_type', 'authorization_code'],
1602
+ ['client_id', clientId],
1603
+ ['redirect_uri', redirectUri]
1604
+ ]);
1605
+ if (pkceCodeVerifier) {
1606
+ body.append('code_verifier', pkceCodeVerifier);
1607
+ }
1608
+ /** @type {Record<string, string>} */
1609
+ const headers = {};
1610
+ if (__classPrivateFieldGet(this, _TideCloak_dpopProvider, "f")) {
1611
+ const nonce = await __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f").getAuthServerNonce();
1612
+ headers['DPoP'] = await __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f").generateDPoPProof(url, 'POST', undefined, nonce);
1613
+ }
1614
+ try {
1615
+ return await fetchJSON(url, {
1616
+ method: 'POST',
1617
+ credentials: 'include',
1618
+ headers,
1619
+ body
1620
+ });
1621
+ }
1622
+ catch (error) {
1623
+ // Handle DPoP nonce retry for token endpoint
1624
+ if (__classPrivateFieldGet(this, _TideCloak_dpopProvider, "f") && error instanceof NetworkError) {
1625
+ const newNonce = error.response.headers.get('DPoP-Nonce');
1626
+ if (newNonce) {
1627
+ await __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f").updateAuthServerNonce(newNonce);
1628
+ headers['DPoP'] = await __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f").generateDPoPProof(url, 'POST', undefined, newNonce);
1629
+ return await fetchJSON(url, {
1630
+ method: 'POST',
1631
+ credentials: 'include',
1632
+ headers,
1633
+ body
1634
+ });
1635
+ }
1636
+ }
1637
+ throw error;
1638
+ }
1639
+ }, _TideCloak_fetchRefreshToken =
1640
+ /**
1641
+ * Fetch the refresh token from the given URL.
1642
+ * @param {string} url
1643
+ * @param {string} refreshToken
1644
+ * @param {string} clientId
1645
+ * @returns {Promise<AccessTokenResponse>}
1646
+ */
1647
+ async function _TideCloak_fetchRefreshToken(url, refreshToken, clientId) {
1648
+ const body = new URLSearchParams([
1649
+ ['grant_type', 'refresh_token'],
1650
+ ['refresh_token', refreshToken],
1651
+ ['client_id', clientId]
1652
+ ]);
1653
+ /** @type {Record<string, string>} */
1654
+ const headers = {};
1655
+ if (__classPrivateFieldGet(this, _TideCloak_dpopProvider, "f")) {
1656
+ const nonce = await __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f").getAuthServerNonce();
1657
+ headers['DPoP'] = await __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f").generateDPoPProof(url, 'POST', undefined, nonce);
1658
+ }
1659
+ try {
1660
+ return await fetchJSON(url, {
1661
+ method: 'POST',
1662
+ credentials: 'include',
1663
+ headers,
1664
+ body
1665
+ });
1666
+ }
1667
+ catch (error) {
1668
+ // Handle DPoP nonce retry for token endpoint
1669
+ if (__classPrivateFieldGet(this, _TideCloak_dpopProvider, "f") && error instanceof NetworkError) {
1670
+ const newNonce = error.response.headers.get('DPoP-Nonce');
1671
+ if (newNonce) {
1672
+ await __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f").updateAuthServerNonce(newNonce);
1673
+ headers['DPoP'] = await __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f").generateDPoPProof(url, 'POST', undefined, newNonce);
1674
+ return await fetchJSON(url, {
1675
+ method: 'POST',
1676
+ credentials: 'include',
1677
+ headers,
1678
+ body
1679
+ });
1680
+ }
1681
+ }
1682
+ throw error;
1683
+ }
1684
+ }, _TideCloak_ensureRequestEnclaveOpen = function _TideCloak_ensureRequestEnclaveOpen() {
1685
+ if (!this.requestEnclave)
1686
+ return;
1687
+ this.requestEnclave.checkEnclaveOpen();
1468
1688
  }, _TideCloak_getVoucherUrl = function _TideCloak_getVoucherUrl() {
1469
1689
  if (!this.tokenParsed)
1470
1690
  throw new Error('User authentication required to access voucher service');
@@ -1660,8 +1880,8 @@ const STORAGE_KEY_PREFIX = 'kc-callback-';
1660
1880
  * @property {string} state
1661
1881
  * @property {string} nonce
1662
1882
  * @property {string} redirectUri
1663
- * @property {KeycloakLoginOptions} [loginOptions]
1664
- * @property {KeycloakLoginOptions['prompt']} [prompt]
1883
+ * @property {TideCloakLoginOptions} [loginOptions]
1884
+ * @property {TideCloakLoginOptions['prompt']} [prompt]
1665
1885
  * @property {string} [pkceCodeVerifier]
1666
1886
  */
1667
1887
  /**
@@ -1849,7 +2069,7 @@ async function sha256Digest(message) {
1849
2069
  }
1850
2070
  /**
1851
2071
  * @param {string} token
1852
- * @returns {KeycloakTokenParsed}
2072
+ * @returns {TideCloakTokenParsed}
1853
2073
  */
1854
2074
  function decodeToken(token) {
1855
2075
  const [, payload] = token.split('.');
@@ -1937,59 +2157,6 @@ async function fetchJsonConfig(url) {
1937
2157
  async function fetchOpenIdConfig(url) {
1938
2158
  return await fetchJSON(url);
1939
2159
  }
1940
- /**
1941
- * @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}.
1942
- * @property {string} access_token The access token issued by the authorization server.
1943
- * @property {string} token_type The type of the token issued by the authorization server.
1944
- * @property {number} [expires_in] The lifetime in seconds of the access token.
1945
- * @property {string} [refresh_token] The refresh token issued by the authorization server.
1946
- * @property {string} [id_token] The ID token issued by the authorization server, if requested.
1947
- * @property {string} [scope] The scope of the access token.
1948
- */
1949
- /**
1950
- * Fetch the access token from the given URL.
1951
- * @param {string} url
1952
- * @param {string} code
1953
- * @param {string} clientId
1954
- * @param {string} redirectUri
1955
- * @param {string} [pkceCodeVerifier]
1956
- * @returns {Promise<AccessTokenResponse>}
1957
- */
1958
- async function fetchAccessToken(url, code, clientId, redirectUri, pkceCodeVerifier) {
1959
- const body = new URLSearchParams([
1960
- ['code', code],
1961
- ['grant_type', 'authorization_code'],
1962
- ['client_id', clientId],
1963
- ['redirect_uri', stripHash(redirectUri)]
1964
- ]);
1965
- if (pkceCodeVerifier) {
1966
- body.append('code_verifier', pkceCodeVerifier);
1967
- }
1968
- return await fetchJSON(url, {
1969
- method: 'POST',
1970
- credentials: 'include',
1971
- body
1972
- });
1973
- }
1974
- /**
1975
- * Fetch the refresh token from the given URL.
1976
- * @param {string} url
1977
- * @param {string} refreshToken
1978
- * @param {string} clientId
1979
- * @returns {Promise<AccessTokenResponse>}
1980
- */
1981
- async function fetchRefreshToken(url, refreshToken, clientId) {
1982
- const body = new URLSearchParams([
1983
- ['grant_type', 'refresh_token'],
1984
- ['refresh_token', refreshToken],
1985
- ['client_id', clientId]
1986
- ]);
1987
- return await fetchJSON(url, {
1988
- method: 'POST',
1989
- credentials: 'include',
1990
- body
1991
- });
1992
- }
1993
2160
  /**
1994
2161
  * @template [T=unknown]
1995
2162
  * @param {string} url
@@ -2034,15 +2201,6 @@ function buildAuthorizationHeader(token) {
2034
2201
  function stripTrailingSlash(url) {
2035
2202
  return url.endsWith('/') ? url.slice(0, -1) : url;
2036
2203
  }
2037
- /**
2038
- * @param {string} url
2039
- * @returns {string}
2040
- */
2041
- function stripHash(url) {
2042
- const parsedUrl = new URL(url);
2043
- parsedUrl.hash = '';
2044
- return parsedUrl.toString();
2045
- }
2046
2204
  /**
2047
2205
  * @typedef {Object} NetworkErrorOptionsProperties
2048
2206
  * @property {Response} response