@tidecloak/js 0.13.14 → 0.13.15

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_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,494 @@ 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
+ return this.authenticated;
233
+ };
234
+ /**
235
+ * @param {TideCloakLoginOptions} [options]
236
+ * @returns {Promise<void>}
237
+ */
238
+ this.login = (options) => {
239
+ return __classPrivateFieldGet(this, _TideCloak_adapter, "f").login(options);
240
+ };
241
+ /**
242
+ * @param {TideCloakLoginOptions} [options]
243
+ * @returns {Promise<string>}
244
+ */
245
+ this.createLoginUrl = async (options) => {
246
+ const state = createUUID();
247
+ const nonce = createUUID();
248
+ const redirectUri = __classPrivateFieldGet(this, _TideCloak_adapter, "f").redirectUri(options);
249
+ /** @type {CallbackState} */
250
+ const callbackState = {
251
+ state,
252
+ nonce,
253
+ redirectUri,
254
+ loginOptions: options
255
+ };
256
+ if (options === null || options === void 0 ? void 0 : options.prompt) {
257
+ callbackState.prompt = options.prompt;
258
+ }
259
+ const url = (options === null || options === void 0 ? void 0 : options.action) === 'register'
260
+ ? this.endpoints.register()
261
+ : this.endpoints.authorize();
262
+ let scope = (options === null || options === void 0 ? void 0 : options.scope) || this.scope;
263
+ const scopeValues = scope ? scope.split(' ') : [];
264
+ // Ensure the 'openid' scope is always included.
265
+ if (!scopeValues.includes('openid')) {
266
+ scopeValues.unshift('openid');
267
+ }
268
+ scope = scopeValues.join(' ');
269
+ const params = new URLSearchParams([
270
+ ['client_id', /** @type {string} */ (this.clientId)],
271
+ ['redirect_uri', redirectUri],
272
+ ['state', state],
273
+ ['response_mode', this.responseMode],
274
+ ['response_type', this.responseType],
275
+ ['scope', scope]
276
+ ]);
277
+ if (__classPrivateFieldGet(this, _TideCloak_useNonce, "f")) {
278
+ params.append('nonce', nonce);
279
+ }
280
+ if (options === null || options === void 0 ? void 0 : options.prompt) {
281
+ params.append('prompt', options.prompt);
282
+ }
283
+ if (typeof (options === null || options === void 0 ? void 0 : options.maxAge) === 'number') {
284
+ params.append('max_age', options.maxAge.toString());
285
+ }
286
+ if (options === null || options === void 0 ? void 0 : options.loginHint) {
287
+ params.append('login_hint', options.loginHint);
288
+ }
289
+ if (options === null || options === void 0 ? void 0 : options.idpHint) {
290
+ params.append('kc_idp_hint', options.idpHint);
291
+ }
292
+ if ((options === null || options === void 0 ? void 0 : options.action) && options.action !== 'register') {
293
+ params.append('kc_action', options.action);
294
+ }
295
+ if (options === null || options === void 0 ? void 0 : options.locale) {
296
+ params.append('ui_locales', options.locale);
297
+ }
298
+ if (options === null || options === void 0 ? void 0 : options.acr) {
299
+ params.append('claims', buildClaimsParameter(options.acr));
300
+ }
301
+ if (options === null || options === void 0 ? void 0 : options.acrValues) {
302
+ params.append('acr_values', options.acrValues);
303
+ }
304
+ if (this.pkceMethod) {
305
+ try {
306
+ const codeVerifier = generateCodeVerifier(96);
307
+ const pkceChallenge = await generatePkceChallenge(this.pkceMethod, codeVerifier);
308
+ callbackState.pkceCodeVerifier = codeVerifier;
309
+ params.append('code_challenge', pkceChallenge);
310
+ params.append('code_challenge_method', this.pkceMethod);
311
+ }
312
+ catch (error) {
313
+ throw new Error('Failed to generate PKCE challenge.', { cause: error });
314
+ }
315
+ }
316
+ __classPrivateFieldGet(this, _TideCloak_callbackStorage, "f").add(callbackState);
317
+ return `${url}?${params.toString()}`;
318
+ };
319
+ /**
320
+ * @param {TideCloakLogoutOptions} [options]
321
+ * @returns {Promise<void>}
322
+ */
323
+ this.logout = async (options) => {
324
+ var _a;
325
+ await ((_a = __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f")) === null || _a === void 0 ? void 0 : _a.flush());
326
+ return __classPrivateFieldGet(this, _TideCloak_adapter, "f").logout(options);
327
+ };
328
+ /**
329
+ * @param {TideCloakLogoutOptions} [options]
330
+ * @returns {string}
331
+ */
332
+ this.createLogoutUrl = (options) => {
333
+ var _a;
334
+ const logoutMethod = (_a = options === null || options === void 0 ? void 0 : options.logoutMethod) !== null && _a !== void 0 ? _a : this.logoutMethod;
335
+ const url = this.endpoints.logout();
336
+ if (logoutMethod === 'POST') {
337
+ return url;
338
+ }
339
+ const params = new URLSearchParams([
340
+ ['client_id', /** @type {string} */ (this.clientId)],
341
+ ['post_logout_redirect_uri', __classPrivateFieldGet(this, _TideCloak_adapter, "f").redirectUri(options)]
342
+ ]);
343
+ if (this.idToken) {
344
+ params.append('id_token_hint', this.idToken);
345
+ }
346
+ return `${url}?${params.toString()}`;
347
+ };
348
+ /**
349
+ * @param {TideCloakRegisterOptions} [options]
350
+ * @returns {Promise<void>}
351
+ */
352
+ this.register = (options) => {
353
+ return __classPrivateFieldGet(this, _TideCloak_adapter, "f").register(options);
354
+ };
355
+ /**
356
+ * @param {TideCloakRegisterOptions} [options]
357
+ * @returns {Promise<string>}
358
+ */
359
+ this.createRegisterUrl = (options) => {
360
+ return this.createLoginUrl({ ...options, action: 'register' });
361
+ };
362
+ /**
363
+ * @param {TideCloakAccountOptions} [options]
364
+ * @returns {string}
365
+ */
366
+ this.createAccountUrl = (options) => {
367
+ const url = __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_getRealmUrl).call(this);
368
+ if (!url) {
369
+ throw new Error('Unable to create account URL, make sure the adapter is not configured using a generic OIDC provider.');
370
+ }
371
+ const params = new URLSearchParams([
372
+ ['referrer', /** @type {string} */ (this.clientId)],
373
+ ['referrer_uri', __classPrivateFieldGet(this, _TideCloak_adapter, "f").redirectUri(options)]
374
+ ]);
375
+ return `${url}/account?${params.toString()}`;
376
+ };
377
+ /**
378
+ * @returns {Promise<void>}
379
+ */
380
+ this.accountManagement = () => {
381
+ return __classPrivateFieldGet(this, _TideCloak_adapter, "f").accountManagement();
382
+ };
383
+ /**
384
+ * @param {string} role
385
+ * @returns {boolean}
386
+ */
387
+ this.hasRealmRole = (role) => {
388
+ const access = this.realmAccess;
389
+ return !!access && access.roles.indexOf(role) >= 0;
390
+ };
391
+ /**
392
+ * @param {string} role
393
+ * @param {string} [resource]
394
+ * @returns {boolean}
395
+ */
396
+ this.hasResourceRole = (role, resource) => {
397
+ if (!this.resourceAccess) {
398
+ return false;
399
+ }
400
+ const access = this.resourceAccess[resource || /** @type {string} */ (this.clientId)];
401
+ return !!access && access.roles.indexOf(role) >= 0;
402
+ };
403
+ /**
404
+ * @returns {Promise<TideCloakProfile>}
405
+ */
406
+ this.loadUserProfile = async () => {
407
+ const realmUrl = __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_getRealmUrl).call(this);
408
+ if (!realmUrl) {
409
+ throw new Error('Unable to load user profile, make sure the adapter is not configured using a generic OIDC provider.');
410
+ }
411
+ const url = `${realmUrl}/account`;
412
+ /** @type {TideCloakProfile} */
413
+ const profile = await fetchJSON(url, {
414
+ headers: [buildAuthorizationHeader(this.token)]
415
+ });
416
+ return (this.profile = profile);
417
+ };
418
+ /**
419
+ * @returns {Promise<{}>}
420
+ */
421
+ this.loadUserInfo = async () => {
422
+ const url = this.endpoints.userinfo();
423
+ /** @type {{}} */
424
+ const userInfo = await fetchJSON(url, {
425
+ headers: [buildAuthorizationHeader(this.token)]
426
+ });
427
+ return (this.userInfo = userInfo);
428
+ };
429
+ /**
430
+ * @param {number} [minValidity]
431
+ * @returns {boolean}
432
+ */
433
+ this.isTokenExpired = (minValidity) => {
434
+ if (!this.tokenParsed || (!this.refreshToken && this.flow !== 'implicit')) {
435
+ throw new Error('Not authenticated');
436
+ }
437
+ if (this.timeSkew == null) {
438
+ __classPrivateFieldGet(this, _TideCloak_logInfo, "f").call(this, '[TIDECLOAK] Unable to determine if token is expired as timeskew is not set');
439
+ return true;
440
+ }
441
+ if (typeof this.tokenParsed.exp !== 'number') {
442
+ return false;
443
+ }
444
+ let expiresIn = this.tokenParsed.exp - Math.ceil(new Date().getTime() / 1000) + this.timeSkew;
445
+ if (minValidity) {
446
+ if (isNaN(minValidity)) {
447
+ throw new Error('Invalid minValidity');
448
+ }
449
+ expiresIn -= minValidity;
450
+ }
451
+ return expiresIn < 0;
452
+ };
453
+ /**
454
+ * @param {number} minValidity
455
+ * @returns {Promise<boolean>}
456
+ */
457
+ this.updateToken = async (minValidity) => {
458
+ var _a, _b;
459
+ if (!this.refreshToken) {
460
+ throw new Error('Unable to update token, no refresh token available.');
461
+ }
462
+ minValidity = minValidity || 5;
463
+ if (__classPrivateFieldGet(this, _TideCloak_loginIframe, "f").enable) {
464
+ await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_checkLoginIframe).call(this);
465
+ }
466
+ let refreshToken = false;
467
+ if (minValidity === -1) {
468
+ refreshToken = true;
469
+ __classPrivateFieldGet(this, _TideCloak_logInfo, "f").call(this, '[TIDECLOAK] Refreshing token: forced refresh');
470
+ }
471
+ else if (!this.tokenParsed || this.isTokenExpired(minValidity)) {
472
+ refreshToken = true;
473
+ __classPrivateFieldGet(this, _TideCloak_logInfo, "f").call(this, '[TIDECLOAK] Refreshing token: token expired');
474
+ }
475
+ if (!refreshToken) {
476
+ return false;
477
+ }
478
+ /** @type {PromiseWithResolvers<boolean>} */
479
+ const { promise, resolve, reject } = Promise.withResolvers();
480
+ __classPrivateFieldGet(this, _TideCloak_refreshQueue, "f").push({ resolve, reject });
481
+ if (__classPrivateFieldGet(this, _TideCloak_refreshQueue, "f").length === 1) {
482
+ const url = this.endpoints.token();
483
+ let timeLocal = new Date().getTime();
484
+ try {
485
+ const response = await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_fetchRefreshToken).call(this, url, this.refreshToken, /** @type {string} */ (this.clientId));
486
+ __classPrivateFieldGet(this, _TideCloak_logInfo, "f").call(this, '[TIDECLOAK] Token refreshed');
487
+ timeLocal = (timeLocal + new Date().getTime()) / 2;
488
+ __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_setToken).call(this, response.access_token, response.refresh_token, response.id_token, timeLocal, response.doken);
489
+ (_a = this.onAuthRefreshSuccess) === null || _a === void 0 ? void 0 : _a.call(this);
490
+ for (let p = __classPrivateFieldGet(this, _TideCloak_refreshQueue, "f").pop(); p != null; p = __classPrivateFieldGet(this, _TideCloak_refreshQueue, "f").pop()) {
491
+ p.resolve(true);
492
+ }
493
+ }
494
+ catch (error) {
495
+ __classPrivateFieldGet(this, _TideCloak_logWarn, "f").call(this, '[TIDECLOAK] Failed to refresh token');
496
+ if (error instanceof NetworkError && error.response.status === 400) {
497
+ this.clearToken();
498
+ }
499
+ (_b = this.onAuthRefreshError) === null || _b === void 0 ? void 0 : _b.call(this);
500
+ for (let p = __classPrivateFieldGet(this, _TideCloak_refreshQueue, "f").pop(); p != null; p = __classPrivateFieldGet(this, _TideCloak_refreshQueue, "f").pop()) {
501
+ p.reject(error);
502
+ }
503
+ }
504
+ }
505
+ return await promise;
506
+ };
507
+ this.clearToken = () => {
508
+ var _a;
509
+ if (this.token) {
510
+ __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_setToken).call(this);
511
+ (_a = this.onAuthLogout) === null || _a === void 0 ? void 0 : _a.call(this);
512
+ if (this.loginRequired) {
513
+ this.login();
514
+ }
515
+ }
516
+ };
517
+ this.secureFetch = async (url, init = {}) => {
518
+ var _a, _b;
519
+ const dpopProvider = __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f");
520
+ if (dpopProvider && this.authenticated && this.token) {
521
+ const existingAuth = new Headers(init.headers).get('Authorization');
522
+ const isOurBearerToken = existingAuth === `Bearer ${this.token}`;
523
+ if (!isOurBearerToken) {
524
+ // Quick escape - didn't put this check in first if statement as it's more expensive than other checks
525
+ return fetch(url, init);
526
+ }
527
+ const urlString = url instanceof URL ? url.href : url.toString();
528
+ const origin = new URL(urlString).origin;
529
+ const method = (_a = init.method) !== null && _a !== void 0 ? _a : 'GET';
530
+ const resourceNonce = dpopProvider.getResourceServerNonce(origin);
531
+ const proof = await dpopProvider.generateDPoPProof(urlString, method, this.token, resourceNonce);
532
+ const headers = new Headers(init.headers);
533
+ headers.set('Authorization', `DPoP ${this.token}`);
534
+ headers.set('DPoP', proof);
535
+ const resp = await fetch(url, { ...init, headers });
536
+ // Check for new nonce in response
537
+ const newNonce = resp.headers.get('DPoP-Nonce');
538
+ if (newNonce) {
539
+ dpopProvider.updateResourceServerNonce(origin, newNonce);
540
+ }
541
+ // https://datatracker.ietf.org/doc/html/rfc9449#section-9
542
+ // Resource servers signal via: WWW-Authenticate: DPoP error="use_dpop_nonce"
543
+ if (resp.status === 401 && newNonce) {
544
+ const wwwAuth = (_b = resp.headers.get('WWW-Authenticate')) !== null && _b !== void 0 ? _b : '';
545
+ if (wwwAuth.includes('DPoP') && wwwAuth.includes('error="use_dpop_nonce"')) {
546
+ const retryProof = await dpopProvider.generateDPoPProof(urlString, method, this.token, newNonce);
547
+ const retryHeaders = new Headers(init.headers);
548
+ retryHeaders.set('Authorization', `DPoP ${this.token}`);
549
+ retryHeaders.set('DPoP', retryProof);
550
+ const retryResp = await fetch(url, { ...init, headers: retryHeaders });
551
+ // Capture nonce from retry response for future requests
552
+ const retryNonce = retryResp.headers.get('DPoP-Nonce');
553
+ if (retryNonce) {
554
+ dpopProvider.updateResourceServerNonce(origin, retryNonce);
555
+ }
556
+ return retryResp;
557
+ }
558
+ }
559
+ return resp;
560
+ }
561
+ else {
562
+ return fetch(url, init);
563
+ }
564
+ };
108
565
  if (typeof config !== 'string' && !isObject(config)) {
109
566
  throw new Error("The 'TideCloak' constructor must be provided with a configuration object, or a URL to a JSON configuration file.");
110
567
  }
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
- // }
568
+ if (isObject(config)) {
569
+ const requiredProperties = 'oidcProvider' in config
570
+ ? ['clientId']
571
+ : ['url', 'realm', 'clientId'];
572
+ for (const property of requiredProperties) {
573
+ if (!(property in config)) {
574
+ throw new Error(`The configuration object is missing the required '${property}' property.`);
575
+ }
576
+ }
577
+ }
121
578
  if (!globalThis.isSecureContext) {
122
579
  __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
580
  'Continuing to run your application insecurely will lead to unexpected behavior and breakage.\n\n' +
@@ -125,113 +582,7 @@ class TideCloak {
125
582
  }
126
583
  __classPrivateFieldSet(this, _TideCloak_config, config, "f");
127
584
  }
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
585
  ;
228
- /**
229
- * @param {KeycloakLoginOptions} [options]
230
- * @returns {Promise<void>}
231
- */
232
- login(options) {
233
- return __classPrivateFieldGet(this, _TideCloak_adapter, "f").login(options);
234
- }
235
586
  /**
236
587
  * Ensure the access token is valid, refreshing if needed.
237
588
  * @returns {Promise<void>}
@@ -243,283 +594,6 @@ class TideCloak {
243
594
  await this.updateToken(-1);
244
595
  }
245
596
  }
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
597
  /**
524
598
  * Initialize Tide RequestEnclave.
525
599
  */
@@ -703,7 +777,7 @@ class TideCloak {
703
777
  return decrypted.map((d, i) => (dataToSend[i].isRaw ? d : StringFromUint8Array(d)));
704
778
  }
705
779
  }
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) {
780
+ _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
781
  if (type === 'default') {
708
782
  return __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_loadDefaultAdapter).call(this);
709
783
  }
@@ -717,7 +791,7 @@ _TideCloak_refreshQueue = new WeakMap(), _TideCloak_adapter = new WeakMap(), _Ti
717
791
  }
718
792
  throw new Error('invalid adapter type: ' + type);
719
793
  }, _TideCloak_loadDefaultAdapter = function _TideCloak_loadDefaultAdapter() {
720
- /** @type {KeycloakAdapter['redirectUri']}{} */
794
+ /** @type {TideCloakAdapter['redirectUri']}{} */
721
795
  const redirectUri = (options) => {
722
796
  return (options === null || options === void 0 ? void 0 : options.redirectUri) || this.redirectUri || globalThis.location.href;
723
797
  };
@@ -1021,7 +1095,7 @@ async function _TideCloak_loadConfig() {
1021
1095
  this.authServerUrl = jsonConfig['auth-server-url'];
1022
1096
  this.realm = jsonConfig.realm;
1023
1097
  this.clientId = jsonConfig.resource;
1024
- __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_setupEndpoints).call(this);
1098
+ await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_setupEndpoints).call(this);
1025
1099
  }
1026
1100
  else {
1027
1101
  this.clientId = __classPrivateFieldGet(this, _TideCloak_config, "f").clientId;
@@ -1031,10 +1105,31 @@ async function _TideCloak_loadConfig() {
1031
1105
  else {
1032
1106
  this.authServerUrl = __classPrivateFieldGet(this, _TideCloak_config, "f").url;
1033
1107
  this.realm = __classPrivateFieldGet(this, _TideCloak_config, "f").realm;
1034
- __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_setupEndpoints).call(this);
1108
+ await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_setupEndpoints).call(this);
1109
+ }
1110
+ }
1111
+ }, _TideCloak_setupEndpoints =
1112
+ /**
1113
+ * @returns {Promise<void>}
1114
+ */
1115
+ async function _TideCloak_setupEndpoints() {
1116
+ // Fetch OIDC metadata to get DPoP supported algorithms (only if DPoP is enabled)
1117
+ if (this.useDPoP) {
1118
+ const issuerUrl = __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_getIssuerUrl).call(this);
1119
+ if (!issuerUrl) {
1120
+ __classPrivateFieldGet(this, _TideCloak_logWarn, "f").call(this, '[TIDECLOAK] Cannot fetch OIDC metadata: issuer URL is not available');
1121
+ }
1122
+ else {
1123
+ const url = `${stripTrailingSlash(issuerUrl)}/.well-known/openid-configuration`;
1124
+ try {
1125
+ const openIdConfig = await fetchOpenIdConfig(url);
1126
+ this.dpopSigningAlgValuesSupported = openIdConfig.dpop_signing_alg_values_supported ? openIdConfig.dpop_signing_alg_values_supported : undefined;
1127
+ }
1128
+ catch (error) {
1129
+ __classPrivateFieldGet(this, _TideCloak_logWarn, "f").call(this, '[TIDECLOAK] Failed to fetch OIDC metadata for DPoP configuration: ' + (error instanceof Error ? error.message : error));
1130
+ }
1035
1131
  }
1036
1132
  }
1037
- }, _TideCloak_setupEndpoints = function _TideCloak_setupEndpoints() {
1038
1133
  this.endpoints = {
1039
1134
  authorize: () => {
1040
1135
  return __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_getRealmUrl).call(this) + '/protocol/openid-connect/auth';
@@ -1073,6 +1168,9 @@ async function _TideCloak_loadOidcConfig(oidcProvider) {
1073
1168
  __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_setupOidcEndpoints).call(this, oidcProvider);
1074
1169
  }
1075
1170
  }, _TideCloak_setupOidcEndpoints = function _TideCloak_setupOidcEndpoints(config) {
1171
+ // Store DPoP supported algorithms if present
1172
+ this.dpopSigningAlgValuesSupported = config.dpop_signing_alg_values_supported;
1173
+ this.issuer = config.issuer;
1076
1174
  this.endpoints = {
1077
1175
  authorize() {
1078
1176
  return config.authorization_endpoint;
@@ -1102,6 +1200,11 @@ async function _TideCloak_loadOidcConfig(oidcProvider) {
1102
1200
  return config.userinfo_endpoint;
1103
1201
  }
1104
1202
  };
1203
+ }, _TideCloak_getIssuerUrl = function _TideCloak_getIssuerUrl() {
1204
+ if (this.issuer)
1205
+ return this.issuer;
1206
+ const realmUrl = __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_getRealmUrl).call(this);
1207
+ return realmUrl || undefined;
1105
1208
  }, _TideCloak_check3pCookiesSupported =
1106
1209
  /**
1107
1210
  * @returns {Promise<void>}
@@ -1147,14 +1250,14 @@ async function _TideCloak_check3pCookiesSupported() {
1147
1250
  return await applyTimeoutToPromise(promise, this.messageReceiveTimeout, 'Timeout when waiting for 3rd party check iframe message.');
1148
1251
  }, _TideCloak_processInit =
1149
1252
  /**
1150
- * @param {KeycloakInitOptions} initOptions
1253
+ * @param {TideCloakInitOptions} initOptions
1151
1254
  * @returns {Promise<void>}
1152
1255
  */
1153
1256
  async function _TideCloak_processInit(initOptions) {
1154
1257
  var _a, _b, _c;
1155
1258
  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);
1259
+ if (callback === null || callback === void 0 ? void 0 : callback.newUrl) {
1260
+ window.history.replaceState(window.history.state, '', callback.newUrl);
1158
1261
  }
1159
1262
  if (callback && callback.valid) {
1160
1263
  await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_setupCheckLoginIframe).call(this);
@@ -1163,7 +1266,7 @@ async function _TideCloak_processInit(initOptions) {
1163
1266
  }
1164
1267
  /** @param {boolean} prompt */
1165
1268
  const doLogin = async (prompt) => {
1166
- /** @type {KeycloakLoginOptions} */
1269
+ /** @type {TideCloakLoginOptions} */
1167
1270
  const options = {};
1168
1271
  if (!prompt) {
1169
1272
  options.prompt = 'none';
@@ -1364,28 +1467,28 @@ async function _TideCloak_checkSsoSilently() {
1364
1467
  supportedParams.push('error_description');
1365
1468
  supportedParams.push('error_uri');
1366
1469
  const url = new URL(urlString);
1367
- let redirectUri = '';
1470
+ let newUrl = '';
1368
1471
  let parsed;
1369
1472
  if (this.responseMode === 'query' && url.searchParams.size > 0) {
1370
1473
  parsed = __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_parseCallbackParams).call(this, url.search, supportedParams);
1371
1474
  url.search = parsed.paramsString;
1372
- redirectUri = url.toString();
1475
+ newUrl = url.toString();
1373
1476
  }
1374
1477
  else if (this.responseMode === 'fragment' && url.hash.length > 0) {
1375
1478
  parsed = __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_parseCallbackParams).call(this, url.hash.substring(1), supportedParams);
1376
- url.hash = '';
1377
- redirectUri = url.toString();
1479
+ url.hash = parsed.paramsString;
1480
+ newUrl = url.toString();
1378
1481
  }
1379
1482
  if (parsed === null || parsed === void 0 ? void 0 : parsed.oauthParams) {
1380
1483
  if (this.flow === 'standard' || this.flow === 'hybrid') {
1381
1484
  if ((parsed.oauthParams.code || parsed.oauthParams.error) && parsed.oauthParams.state) {
1382
- parsed.oauthParams.redirectUri = redirectUri;
1485
+ parsed.oauthParams.newUrl = newUrl;
1383
1486
  return parsed.oauthParams;
1384
1487
  }
1385
1488
  }
1386
1489
  else if (this.flow === 'implicit') {
1387
1490
  if ((parsed.oauthParams.access_token || parsed.oauthParams.error) && parsed.oauthParams.state) {
1388
- parsed.oauthParams.redirectUri = redirectUri;
1491
+ parsed.oauthParams.newUrl = newUrl;
1389
1492
  return parsed.oauthParams;
1390
1493
  }
1391
1494
  }
@@ -1445,7 +1548,7 @@ async function _TideCloak_checkSsoSilently() {
1445
1548
  }
1446
1549
  if ((this.flow !== 'implicit') && code) {
1447
1550
  try {
1448
- const response = await fetchAccessToken(this.endpoints.token(), code, /** @type {string} */ (this.clientId), oauth.redirectUri, oauth.pkceCodeVerifier);
1551
+ 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
1552
  authSuccess(response.access_token, response.refresh_token, response.id_token, response.doken);
1450
1553
  if (this.flow === 'standard') {
1451
1554
  (_c = this.onAuthSuccess) === null || _c === void 0 ? void 0 : _c.call(this);
@@ -1465,6 +1568,112 @@ async function _TideCloak_checkSsoSilently() {
1465
1568
  await __classPrivateFieldGet(this, _TideCloak_instances, "m", _TideCloak_scheduleCheckIframe).call(this);
1466
1569
  }
1467
1570
  }
1571
+ }, _TideCloak_fetchAccessToken =
1572
+ /**
1573
+ * @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}.
1574
+ * @property {string} access_token The access token issued by the authorization server.
1575
+ * @property {string} token_type The type of the token issued by the authorization server.
1576
+ * @property {number} [expires_in] The lifetime in seconds of the access token.
1577
+ * @property {string} [refresh_token] The refresh token issued by the authorization server.
1578
+ * @property {string} [id_token] The ID token issued by the authorization server, if requested.
1579
+ * @property {string} [scope] The scope of the access token.
1580
+ * @property {string} [doken] The Tide doken issued by the authorization server.
1581
+ */
1582
+ /**
1583
+ * Fetch the access token from the given URL.
1584
+ * @param {string} url
1585
+ * @param {string} code
1586
+ * @param {string} clientId
1587
+ * @param {string} redirectUri
1588
+ * @param {string} [pkceCodeVerifier]
1589
+ * @returns {Promise<AccessTokenResponse>}
1590
+ */
1591
+ async function _TideCloak_fetchAccessToken(url, code, clientId, redirectUri, pkceCodeVerifier) {
1592
+ const body = new URLSearchParams([
1593
+ ['code', code],
1594
+ ['grant_type', 'authorization_code'],
1595
+ ['client_id', clientId],
1596
+ ['redirect_uri', redirectUri]
1597
+ ]);
1598
+ if (pkceCodeVerifier) {
1599
+ body.append('code_verifier', pkceCodeVerifier);
1600
+ }
1601
+ /** @type {Record<string, string>} */
1602
+ const headers = {};
1603
+ if (__classPrivateFieldGet(this, _TideCloak_dpopProvider, "f")) {
1604
+ const nonce = await __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f").getAuthServerNonce();
1605
+ headers['DPoP'] = await __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f").generateDPoPProof(url, 'POST', undefined, nonce);
1606
+ }
1607
+ try {
1608
+ return await fetchJSON(url, {
1609
+ method: 'POST',
1610
+ credentials: 'include',
1611
+ headers,
1612
+ body
1613
+ });
1614
+ }
1615
+ catch (error) {
1616
+ // Handle DPoP nonce retry for token endpoint
1617
+ if (__classPrivateFieldGet(this, _TideCloak_dpopProvider, "f") && error instanceof NetworkError) {
1618
+ const newNonce = error.response.headers.get('DPoP-Nonce');
1619
+ if (newNonce) {
1620
+ await __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f").updateAuthServerNonce(newNonce);
1621
+ headers['DPoP'] = await __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f").generateDPoPProof(url, 'POST', undefined, newNonce);
1622
+ return await fetchJSON(url, {
1623
+ method: 'POST',
1624
+ credentials: 'include',
1625
+ headers,
1626
+ body
1627
+ });
1628
+ }
1629
+ }
1630
+ throw error;
1631
+ }
1632
+ }, _TideCloak_fetchRefreshToken =
1633
+ /**
1634
+ * Fetch the refresh token from the given URL.
1635
+ * @param {string} url
1636
+ * @param {string} refreshToken
1637
+ * @param {string} clientId
1638
+ * @returns {Promise<AccessTokenResponse>}
1639
+ */
1640
+ async function _TideCloak_fetchRefreshToken(url, refreshToken, clientId) {
1641
+ const body = new URLSearchParams([
1642
+ ['grant_type', 'refresh_token'],
1643
+ ['refresh_token', refreshToken],
1644
+ ['client_id', clientId]
1645
+ ]);
1646
+ /** @type {Record<string, string>} */
1647
+ const headers = {};
1648
+ if (__classPrivateFieldGet(this, _TideCloak_dpopProvider, "f")) {
1649
+ const nonce = await __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f").getAuthServerNonce();
1650
+ headers['DPoP'] = await __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f").generateDPoPProof(url, 'POST', undefined, nonce);
1651
+ }
1652
+ try {
1653
+ return await fetchJSON(url, {
1654
+ method: 'POST',
1655
+ credentials: 'include',
1656
+ headers,
1657
+ body
1658
+ });
1659
+ }
1660
+ catch (error) {
1661
+ // Handle DPoP nonce retry for token endpoint
1662
+ if (__classPrivateFieldGet(this, _TideCloak_dpopProvider, "f") && error instanceof NetworkError) {
1663
+ const newNonce = error.response.headers.get('DPoP-Nonce');
1664
+ if (newNonce) {
1665
+ await __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f").updateAuthServerNonce(newNonce);
1666
+ headers['DPoP'] = await __classPrivateFieldGet(this, _TideCloak_dpopProvider, "f").generateDPoPProof(url, 'POST', undefined, newNonce);
1667
+ return await fetchJSON(url, {
1668
+ method: 'POST',
1669
+ credentials: 'include',
1670
+ headers,
1671
+ body
1672
+ });
1673
+ }
1674
+ }
1675
+ throw error;
1676
+ }
1468
1677
  }, _TideCloak_getVoucherUrl = function _TideCloak_getVoucherUrl() {
1469
1678
  if (!this.tokenParsed)
1470
1679
  throw new Error('User authentication required to access voucher service');
@@ -1660,8 +1869,8 @@ const STORAGE_KEY_PREFIX = 'kc-callback-';
1660
1869
  * @property {string} state
1661
1870
  * @property {string} nonce
1662
1871
  * @property {string} redirectUri
1663
- * @property {KeycloakLoginOptions} [loginOptions]
1664
- * @property {KeycloakLoginOptions['prompt']} [prompt]
1872
+ * @property {TideCloakLoginOptions} [loginOptions]
1873
+ * @property {TideCloakLoginOptions['prompt']} [prompt]
1665
1874
  * @property {string} [pkceCodeVerifier]
1666
1875
  */
1667
1876
  /**
@@ -1849,7 +2058,7 @@ async function sha256Digest(message) {
1849
2058
  }
1850
2059
  /**
1851
2060
  * @param {string} token
1852
- * @returns {KeycloakTokenParsed}
2061
+ * @returns {TideCloakTokenParsed}
1853
2062
  */
1854
2063
  function decodeToken(token) {
1855
2064
  const [, payload] = token.split('.');
@@ -1937,59 +2146,6 @@ async function fetchJsonConfig(url) {
1937
2146
  async function fetchOpenIdConfig(url) {
1938
2147
  return await fetchJSON(url);
1939
2148
  }
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
2149
  /**
1994
2150
  * @template [T=unknown]
1995
2151
  * @param {string} url
@@ -2034,15 +2190,6 @@ function buildAuthorizationHeader(token) {
2034
2190
  function stripTrailingSlash(url) {
2035
2191
  return url.endsWith('/') ? url.slice(0, -1) : url;
2036
2192
  }
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
2193
  /**
2047
2194
  * @typedef {Object} NetworkErrorOptionsProperties
2048
2195
  * @property {Response} response