@axa-fr/react-oidc 6.11.4-alpha2 → 6.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/dist/OidcProvider.d.ts +1 -1
  2. package/dist/OidcProvider.d.ts.map +1 -1
  3. package/dist/OidcSecure.d.ts +1 -1
  4. package/dist/OidcSecure.d.ts.map +1 -1
  5. package/dist/OidcServiceWorker.js +16 -3
  6. package/dist/ReactOidc.d.ts +1 -1
  7. package/dist/ReactOidc.d.ts.map +1 -1
  8. package/dist/index.d.ts +1 -1
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js.map +1 -1
  11. package/dist/vanilla/initSession.d.ts +3 -3
  12. package/dist/vanilla/initSession.d.ts.map +1 -1
  13. package/dist/vanilla/initSession.js +20 -20
  14. package/dist/vanilla/initSession.js.map +1 -1
  15. package/dist/vanilla/initWorker.d.ts +4 -4
  16. package/dist/vanilla/initWorker.d.ts.map +1 -1
  17. package/dist/vanilla/initWorker.js +13 -13
  18. package/dist/vanilla/initWorker.js.map +1 -1
  19. package/dist/vanilla/oidc.d.ts +36 -7
  20. package/dist/vanilla/oidc.d.ts.map +1 -1
  21. package/dist/vanilla/oidc.js +357 -82
  22. package/dist/vanilla/oidc.js.map +1 -1
  23. package/dist/vanilla/requests.d.ts +0 -2
  24. package/dist/vanilla/requests.d.ts.map +1 -1
  25. package/dist/vanilla/requests.js +1 -20
  26. package/dist/vanilla/requests.js.map +1 -1
  27. package/dist/vanilla/route-utils.js +1 -1
  28. package/dist/vanilla/route-utils.js.map +1 -1
  29. package/dist/vanilla/vanillaOidc.d.ts +1 -2
  30. package/dist/vanilla/vanillaOidc.d.ts.map +1 -1
  31. package/dist/vanilla/vanillaOidc.js.map +1 -1
  32. package/package.json +1 -1
  33. package/src/oidc/OidcProvider.tsx +1 -1
  34. package/src/oidc/OidcSecure.tsx +1 -1
  35. package/src/oidc/ReactOidc.tsx +1 -1
  36. package/src/oidc/index.ts +1 -1
  37. package/src/oidc/vanilla/OidcServiceWorker.js +16 -3
  38. package/src/oidc/vanilla/index.ts +1 -1
  39. package/src/oidc/vanilla/initSession.ts +20 -20
  40. package/src/oidc/vanilla/initWorker.ts +14 -14
  41. package/src/oidc/vanilla/oidc.ts +380 -52
  42. package/src/oidc/vanilla/requests.ts +0 -24
  43. package/src/oidc/vanilla/route-utils.ts +1 -1
  44. package/src/oidc/vanilla/vanillaOidc.ts +1 -2
  45. package/dist/vanilla/checkSession.d.ts +0 -5
  46. package/dist/vanilla/checkSession.d.ts.map +0 -1
  47. package/dist/vanilla/checkSession.js +0 -68
  48. package/dist/vanilla/checkSession.js.map +0 -1
  49. package/dist/vanilla/events.d.ts +0 -29
  50. package/dist/vanilla/events.d.ts.map +0 -1
  51. package/dist/vanilla/events.js +0 -32
  52. package/dist/vanilla/events.js.map +0 -1
  53. package/dist/vanilla/login.d.ts +0 -4
  54. package/dist/vanilla/login.d.ts.map +0 -1
  55. package/dist/vanilla/login.js +0 -125
  56. package/dist/vanilla/login.js.map +0 -1
  57. package/dist/vanilla/silentLogin.d.ts +0 -8
  58. package/dist/vanilla/silentLogin.d.ts.map +0 -1
  59. package/dist/vanilla/silentLogin.js +0 -95
  60. package/dist/vanilla/silentLogin.js.map +0 -1
  61. package/dist/vanilla/types.d.ts +0 -33
  62. package/dist/vanilla/types.d.ts.map +0 -1
  63. package/dist/vanilla/types.js +0 -3
  64. package/dist/vanilla/types.js.map +0 -1
  65. package/dist/vanilla/user.d.ts +0 -2
  66. package/dist/vanilla/user.d.ts.map +0 -1
  67. package/dist/vanilla/user.js +0 -48
  68. package/dist/vanilla/user.js.map +0 -1
  69. package/src/oidc/vanilla/checkSession.ts +0 -55
  70. package/src/oidc/vanilla/events.ts +0 -29
  71. package/src/oidc/vanilla/login.ts +0 -118
  72. package/src/oidc/vanilla/silentLogin.ts +0 -102
  73. package/src/oidc/vanilla/types.ts +0 -35
  74. package/src/oidc/vanilla/user.ts +0 -39
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  AuthorizationNotifier,
3
+ AuthorizationRequest,
3
4
  AuthorizationServiceConfiguration,
4
5
  BaseTokenRequestHandler,
5
6
  DefaultCrypto,
@@ -11,26 +12,31 @@ import {
11
12
  } from '@openid/appauth';
12
13
  import { AuthorizationServiceConfigurationJson } from '@openid/appauth/src/authorization_service_configuration';
13
14
 
14
- import { startCheckSessionAsync as defaultStartCheckSessionAsync } from './checkSession';
15
+ import { getFromCache, setCache } from './cache';
15
16
  import { CheckSessionIFrame } from './checkSessionIFrame';
16
- import { eventNames } from './events';
17
17
  import { initSession } from './initSession';
18
18
  import { initWorkerAsync, sleepAsync } from './initWorker';
19
- import { defaultLoginAsync, defaultSilentLoginAsync2 } from './login';
20
19
  import { MemoryStorageBackend } from './memoryStorageBackend';
21
20
  import { HashQueryStringUtils, NoHashQueryStringUtils } from './noHashQueryStringUtils';
22
21
  import {
23
22
  computeTimeLeft,
24
23
  isTokensOidcValid,
24
+ isTokensValid,
25
25
  setTokens, TokenRenewMode,
26
26
  Tokens,
27
27
  } from './parseTokens';
28
- import { fetchFromIssuer, performRevocationRequestAsync, performTokenRequestAsync, TOKEN_TYPE } from './requests';
28
+ import { performRevocationRequestAsync, performTokenRequestAsync, TOKEN_TYPE } from './requests';
29
29
  import { getParseQueryStringFromLocation } from './route-utils';
30
- import defaultSilentLoginAsync from './silentLogin';
31
30
  import timer from './timer';
32
- import { AuthorityConfiguration, OidcConfiguration, StringMap } from './types';
33
- import { userInfoAsync } from './user';
31
+
32
+ const randomString = function(length) {
33
+ let text = '';
34
+ const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
35
+ for (let i = 0; i < length; i++) {
36
+ text += possible.charAt(Math.floor(Math.random() * possible.length));
37
+ }
38
+ return text;
39
+ };
34
40
 
35
41
  export interface OidcAuthorizationServiceConfigurationJson extends AuthorizationServiceConfigurationJson{
36
42
  check_session_iframe?: string;
@@ -52,6 +58,41 @@ export class OidcAuthorizationServiceConfiguration extends AuthorizationServiceC
52
58
  }
53
59
  }
54
60
 
61
+ export interface StringMap {
62
+ [key: string]: string;
63
+ }
64
+
65
+ export interface AuthorityConfiguration {
66
+ authorization_endpoint: string;
67
+ token_endpoint: string;
68
+ revocation_endpoint: string;
69
+ end_session_endpoint?: string;
70
+ userinfo_endpoint?: string;
71
+ check_session_iframe?:string;
72
+ issuer:string;
73
+ }
74
+
75
+ export type OidcConfiguration = {
76
+ client_id: string;
77
+ redirect_uri: string;
78
+ silent_redirect_uri?:string;
79
+ silent_login_uri?:string;
80
+ silent_login_timeout?:number;
81
+ scope: string;
82
+ authority: string;
83
+ authority_time_cache_wellknowurl_in_second?: number;
84
+ authority_configuration?: AuthorityConfiguration;
85
+ refresh_time_before_tokens_expiration_in_second?: number;
86
+ token_request_timeout?: number;
87
+ service_worker_relative_url?:string;
88
+ service_worker_only?:boolean;
89
+ extras?:StringMap;
90
+ token_request_extras?:StringMap;
91
+ storage?: Storage;
92
+ monitor_session?: boolean;
93
+ token_renew_mode?: string;
94
+ };
95
+
55
96
  const oidcDatabase = {};
56
97
  const oidcFactory = (configuration: OidcConfiguration, name = 'default') => {
57
98
  if (oidcDatabase[name]) {
@@ -79,9 +120,9 @@ async function renewTokensAndStartTimerAsync(oidc, refreshToken, forceRefresh =
79
120
  const updateTokens = (tokens) => { oidc.tokens = tokens; };
80
121
  const { tokens, status } = await oidc.synchroniseTokensAsync(refreshToken, 0, forceRefresh, extras, updateTokens);
81
122
 
82
- const serviceWorker = await initWorkerAsync(oidc.configuration.service_worker_relative_url, oidc.configurationName);
123
+ const serviceWorker = await initWorkerAsync(oidc.configuration.service_worker_relative_url, oidc.configurationName, oidc.configuration.redirect_uri);
83
124
  if (!serviceWorker) {
84
- const session = initSession(oidc.configurationName, oidc.configuration.storage);
125
+ const session = initSession(oidc.configurationName, oidc.configuration.redirect_uri, oidc.configuration.storage);
85
126
  await session.setTokens(oidc.tokens);
86
127
  }
87
128
 
@@ -105,17 +146,105 @@ const autoRenewTokens = (oidc, refreshToken, expiresAt, extras:StringMap = null)
105
146
  }, 1000);
106
147
  };
107
148
 
149
+ const userInfoAsync = async (oidc) => {
150
+ if (oidc.userInfo != null) {
151
+ return oidc.userInfo;
152
+ }
153
+ if (!oidc.tokens) {
154
+ return null;
155
+ }
156
+ const accessToken = oidc.tokens.accessToken;
157
+ if (!accessToken) {
158
+ return null;
159
+ }
160
+
161
+ // We wait the synchronisation before making a request
162
+ while (oidc.tokens && !isTokensValid(oidc.tokens)) {
163
+ await sleepAsync(200);
164
+ }
165
+
166
+ const oidcServerConfiguration = await oidc.initAsync(oidc.configuration.authority, oidc.configuration.authority_configuration);
167
+ const url = oidcServerConfiguration.userInfoEndpoint;
168
+ const fetchUserInfo = async (accessToken) => {
169
+ const res = await fetch(url, {
170
+ headers: {
171
+ authorization: `Bearer ${accessToken}`,
172
+ },
173
+ });
174
+
175
+ if (res.status !== 200) {
176
+ return null;
177
+ }
178
+
179
+ return res.json();
180
+ };
181
+ const userInfo = await fetchUserInfo(accessToken);
182
+ oidc.userInfo = userInfo;
183
+ return userInfo;
184
+ };
185
+
186
+ const eventNames = {
187
+ service_worker_not_supported_by_browser: 'service_worker_not_supported_by_browser',
188
+ token_aquired: 'token_aquired',
189
+ logout_from_another_tab: 'logout_from_another_tab',
190
+ logout_from_same_tab: 'logout_from_same_tab',
191
+ token_renewed: 'token_renewed',
192
+ token_timer: 'token_timer',
193
+ loginAsync_begin: 'loginAsync_begin',
194
+ loginAsync_error: 'loginAsync_error',
195
+ loginCallbackAsync_begin: 'loginCallbackAsync_begin',
196
+ loginCallbackAsync_end: 'loginCallbackAsync_end',
197
+ loginCallbackAsync_error: 'loginCallbackAsync_error',
198
+ refreshTokensAsync_begin: 'refreshTokensAsync_begin',
199
+ refreshTokensAsync: 'refreshTokensAsync',
200
+ refreshTokensAsync_end: 'refreshTokensAsync_end',
201
+ refreshTokensAsync_error: 'refreshTokensAsync_error',
202
+ refreshTokensAsync_silent_error: 'refreshTokensAsync_silent_error',
203
+ tryKeepExistingSessionAsync_begin: 'tryKeepExistingSessionAsync_begin',
204
+ tryKeepExistingSessionAsync_end: 'tryKeepExistingSessionAsync_end',
205
+ tryKeepExistingSessionAsync_error: 'tryKeepExistingSessionAsync_error',
206
+ silentLoginAsync_begin: 'silentLoginAsync_begin',
207
+ silentLoginAsync: 'silentLoginAsync',
208
+ silentLoginAsync_end: 'silentLoginAsync_end',
209
+ silentLoginAsync_error: 'silentLoginAsync_error',
210
+ syncTokensAsync_begin: 'syncTokensAsync_begin',
211
+ syncTokensAsync_end: 'syncTokensAsync_end',
212
+ syncTokensAsync_error: 'syncTokensAsync_error',
213
+ };
214
+
108
215
  const getRandomInt = (max) => {
109
216
  return Math.floor(Math.random() * max);
110
217
  };
111
218
 
219
+ const oneHourSecond = 60 * 60;
220
+ const fetchFromIssuer = async (openIdIssuerUrl: string, timeCacheSecond = oneHourSecond, storage = window.sessionStorage):
221
+ Promise<OidcAuthorizationServiceConfiguration> => {
222
+ const fullUrl = `${openIdIssuerUrl}/.well-known/openid-configuration`;
223
+
224
+ const localStorageKey = `oidc.server:${openIdIssuerUrl}`;
225
+ const data = getFromCache(localStorageKey, storage, timeCacheSecond);
226
+ if (data) {
227
+ return new OidcAuthorizationServiceConfiguration(data);
228
+ }
229
+ const response = await fetch(fullUrl);
230
+
231
+ if (response.status !== 200) {
232
+ return null;
233
+ }
234
+
235
+ const result = await response.json();
236
+
237
+ setCache(localStorageKey, result, storage);
238
+ return new OidcAuthorizationServiceConfiguration(result);
239
+ };
240
+
112
241
  export class Oidc {
113
242
  public configuration: OidcConfiguration;
114
243
  public userInfo: null;
115
244
  public tokens?: Tokens;
116
245
  public events: Array<any>;
117
246
  private timeoutId: NodeJS.Timeout;
118
- public configurationName: string;
247
+ private configurationName: string;
119
248
  private checkSessionIFrame: CheckSessionIFrame;
120
249
  constructor(configuration:OidcConfiguration, configurationName = 'default') {
121
250
  let silent_login_uri = configuration.silent_login_uri;
@@ -206,7 +335,96 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
206
335
  }
207
336
 
208
337
  async silentLoginAsync(extras:StringMap = null, state:string = null, scope:string = null) {
209
- return defaultSilentLoginAsync(this.configurationName, this.configuration, this.publishEvent.bind(this))(extras, state, scope);
338
+ if (!this.configuration.silent_redirect_uri || !this.configuration.silent_login_uri) {
339
+ return Promise.resolve(null);
340
+ }
341
+
342
+ try {
343
+ this.publishEvent(eventNames.silentLoginAsync_begin, {});
344
+ const configuration = this.configuration;
345
+ let queries = '';
346
+
347
+ if (state) {
348
+ if (extras == null) {
349
+ extras = {};
350
+ }
351
+ extras.state = state;
352
+ }
353
+
354
+ if (scope) {
355
+ if (extras == null) {
356
+ extras = {};
357
+ }
358
+ extras.scope = scope;
359
+ }
360
+
361
+ if (extras != null) {
362
+ for (const [key, value] of Object.entries(extras)) {
363
+ if (queries === '') {
364
+ queries = `?${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
365
+ } else {
366
+ queries += `&${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
367
+ }
368
+ }
369
+ }
370
+ const link = configuration.silent_login_uri + queries;
371
+ const idx = link.indexOf('/', link.indexOf('//') + 2);
372
+ const iFrameOrigin = link.substr(0, idx);
373
+ const iframe = document.createElement('iframe');
374
+ iframe.width = '0px';
375
+ iframe.height = '0px';
376
+
377
+ iframe.id = `${this.configurationName}_oidc_iframe`;
378
+ iframe.setAttribute('src', link);
379
+ document.body.appendChild(iframe);
380
+ return new Promise((resolve, reject) => {
381
+ try {
382
+ let isResolved = false;
383
+ window.onmessage = (e: MessageEvent<any>) => {
384
+ if (e.origin === iFrameOrigin &&
385
+ e.source === iframe.contentWindow
386
+ ) {
387
+ const key = `${this.configurationName}_oidc_tokens:`;
388
+ const key_error = `${this.configurationName}_oidc_error:`;
389
+ const data = e.data;
390
+ if (data && typeof (data) === 'string') {
391
+ if (!isResolved) {
392
+ if (data.startsWith(key)) {
393
+ const result = JSON.parse(e.data.replace(key, ''));
394
+ this.publishEvent(eventNames.silentLoginAsync_end, {});
395
+ iframe.remove();
396
+ isResolved = true;
397
+ resolve(result);
398
+ } else if (data.startsWith(key_error)) {
399
+ const result = JSON.parse(e.data.replace(key_error, ''));
400
+ this.publishEvent(eventNames.silentLoginAsync_error, result);
401
+ iframe.remove();
402
+ isResolved = true;
403
+ reject(new Error('oidc_' + result.error));
404
+ }
405
+ }
406
+ }
407
+ }
408
+ };
409
+ const silentSigninTimeout = configuration.silent_login_timeout;
410
+ setTimeout(() => {
411
+ if (!isResolved) {
412
+ this.publishEvent(eventNames.silentLoginAsync_error, { reason: 'timeout' });
413
+ iframe.remove();
414
+ isResolved = true;
415
+ reject(new Error('timeout'));
416
+ }
417
+ }, silentSigninTimeout);
418
+ } catch (e) {
419
+ iframe.remove();
420
+ this.publishEvent(eventNames.silentLoginAsync_error, e);
421
+ reject(e);
422
+ }
423
+ });
424
+ } catch (e) {
425
+ this.publishEvent(eventNames.silentLoginAsync_error, e);
426
+ throw e;
427
+ }
210
428
  }
211
429
 
212
430
  initPromise = null;
@@ -227,7 +445,7 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
227
445
  });
228
446
  }
229
447
 
230
- const serviceWorker = await initWorkerAsync(this.configuration.service_worker_relative_url, this.configurationName);
448
+ const serviceWorker = await initWorkerAsync(this.configuration.service_worker_relative_url, this.configurationName, this.configuration.redirect_uri);
231
449
  const storage = serviceWorker ? window.localStorage : null;
232
450
  return await fetchFromIssuer(authority, this.configuration.authority_time_cache_wellknowurl_in_second ?? 60 * 60, storage);
233
451
  };
@@ -252,7 +470,7 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
252
470
  try {
253
471
  const configuration = this.configuration;
254
472
  const oidcServerConfiguration = await this.initAsync(configuration.authority, configuration.authority_configuration);
255
- serviceWorker = await initWorkerAsync(configuration.service_worker_relative_url, this.configurationName);
473
+ serviceWorker = await initWorkerAsync(configuration.service_worker_relative_url, this.configurationName, configuration.redirect_uri);
256
474
  if (serviceWorker) {
257
475
  const { tokens } = await serviceWorker.initAsync(oidcServerConfiguration, 'tryKeepExistingSessionAsync', configuration);
258
476
  if (tokens) {
@@ -280,7 +498,7 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
280
498
  message: 'service worker is not supported by this browser',
281
499
  });
282
500
  }
283
- const session = initSession(this.configurationName, configuration.storage ?? sessionStorage);
501
+ const session = initSession(this.configurationName, configuration.redirect_uri, configuration.storage ?? sessionStorage);
284
502
  const { tokens } = await session.initAsync();
285
503
  if (tokens) {
286
504
  // @ts-ignore
@@ -319,26 +537,146 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
319
537
  });
320
538
  }
321
539
 
322
- async startCheckSessionAsync(checkSessionIFrameUri, clientId, sessionState, isSilentSignin = false) {
323
- const getCurrentTokens = () => this.tokens;
324
- this.checkSessionIFrame = await defaultStartCheckSessionAsync(oidcDatabase, this.configuration, this.checkSessionIFrame, this.silentLoginAsync.bind(this), getCurrentTokens)(checkSessionIFrameUri, clientId, sessionState, isSilentSignin);
325
- }
326
-
327
540
  loginPromise: Promise<void> = null;
328
541
  async loginAsync(callbackPath:string = undefined, extras:StringMap = null, isSilentSignin = false, scope:string = undefined, silentLoginOnly = false) {
329
542
  if (this.loginPromise !== null) {
330
543
  return this.loginPromise;
331
544
  }
332
- if (silentLoginOnly) {
333
- return defaultSilentLoginAsync2(window, this.configurationName, this.configuration, this.publishEvent.bind(this), this)(extras, scope);
334
- }
335
- this.loginPromise = defaultLoginAsync(window, this.configurationName, this.configuration, this.silentLoginAsync.bind(this), this.publishEvent.bind(this), this.initAsync.bind(this))(callbackPath, extras, isSilentSignin, scope);
545
+ const loginLocalAsync = async () => {
546
+ const location = window.location;
547
+ const url = callbackPath || location.pathname + (location.search || '') + (location.hash || '');
548
+ const configuration = this.configuration;
549
+ let state;
550
+ if (extras && 'state' in extras) {
551
+ state = extras.state;
552
+ delete extras.state;
553
+ }
554
+ if (silentLoginOnly) {
555
+ try {
556
+ const extraFinal = extras ?? configuration.extras ?? {};
557
+ const silentResult = await this.silentLoginAsync({
558
+ ...extraFinal,
559
+ prompt: 'none',
560
+ }, state, scope);
561
+
562
+ if (silentResult) {
563
+ this.tokens = silentResult.tokens;
564
+ this.publishEvent(eventNames.token_aquired, {});
565
+ // @ts-ignore
566
+ this.timeoutId = autoRenewTokens(this, this.tokens.refreshToken, this.tokens.expiresAt, extras);
567
+ return {};
568
+ }
569
+ } catch (e) {
570
+ return e;
571
+ }
572
+ }
573
+ this.publishEvent(eventNames.loginAsync_begin, {});
574
+
575
+ try {
576
+ const redirectUri = isSilentSignin ? configuration.silent_redirect_uri : configuration.redirect_uri;
577
+ if (!scope) {
578
+ scope = configuration.scope;
579
+ }
580
+
581
+ const extraFinal = extras ?? configuration.extras ?? {};
582
+ if (!extraFinal.nonce) {
583
+ extraFinal.nonce = randomString(12);
584
+ }
585
+ const nonce = { nonce: extraFinal.nonce };
586
+ const serviceWorker = await initWorkerAsync(configuration.service_worker_relative_url, this.configurationName, this.configuration.redirect_uri);
587
+ const oidcServerConfiguration = await this.initAsync(configuration.authority, configuration.authority_configuration);
588
+ let storage;
589
+ if (serviceWorker) {
590
+ serviceWorker.setLoginParams(this.configurationName, redirectUri, { callbackPath: url, extras, state });
591
+ serviceWorker.startKeepAliveServiceWorker();
592
+ await serviceWorker.initAsync(oidcServerConfiguration, 'loginAsync', configuration);
593
+ await serviceWorker.setNonceAsync(nonce);
594
+ storage = new MemoryStorageBackend(serviceWorker.saveItemsAsync, {});
595
+ await storage.setItem('dummy', {});
596
+ } else {
597
+ const session = initSession(this.configurationName, redirectUri);
598
+ session.setLoginParams(this.configurationName, redirectUri, { callbackPath: url, extras, state });
599
+ await session.setNonceAsync(nonce);
600
+ storage = new MemoryStorageBackend(session.saveItemsAsync, {});
601
+ }
602
+
603
+ // @ts-ignore
604
+ const queryStringUtil = redirectUri.includes('#') ? new HashQueryStringUtils() : new NoHashQueryStringUtils();
605
+ const authorizationHandler = new RedirectRequestHandler(storage, queryStringUtil, window.location, new DefaultCrypto());
606
+ const authRequest = new AuthorizationRequest({
607
+ client_id: configuration.client_id,
608
+ redirect_uri: redirectUri,
609
+ scope,
610
+ response_type: AuthorizationRequest.RESPONSE_TYPE_CODE,
611
+ state,
612
+ extras: extraFinal,
613
+ });
614
+ authorizationHandler.performAuthorizationRequest(oidcServerConfiguration, authRequest);
615
+ } catch (exception) {
616
+ this.publishEvent(eventNames.loginAsync_error, exception);
617
+ throw exception;
618
+ }
619
+ };
620
+ this.loginPromise = loginLocalAsync();
336
621
  return this.loginPromise.then(result => {
337
622
  this.loginPromise = null;
338
623
  return result;
339
624
  });
340
625
  }
341
626
 
627
+ async startCheckSessionAsync(checkSessionIFrameUri, clientId, sessionState, isSilentSignin = false) {
628
+ return new Promise<void>((resolve, reject): void => {
629
+ if (this.configuration.silent_login_uri && this.configuration.silent_redirect_uri && this.configuration.monitor_session && checkSessionIFrameUri && sessionState && !isSilentSignin) {
630
+ const checkSessionCallback = () => {
631
+ this.checkSessionIFrame.stop();
632
+
633
+ if (this.tokens === null) {
634
+ return;
635
+ }
636
+ // @ts-ignore
637
+ const idToken = this.tokens.idToken;
638
+ // @ts-ignore
639
+ const idTokenPayload = this.tokens.idTokenPayload;
640
+ this.silentLoginAsync({
641
+ prompt: 'none',
642
+ id_token_hint: idToken,
643
+ scope: 'openid',
644
+ }).then((silentSigninResponse) => {
645
+ const iFrameIdTokenPayload = silentSigninResponse.tokens.idTokenPayload;
646
+ if (idTokenPayload.sub === iFrameIdTokenPayload.sub) {
647
+ const sessionState = silentSigninResponse.sessionState;
648
+ this.checkSessionIFrame.start(silentSigninResponse.sessionState);
649
+ if (idTokenPayload.sid === iFrameIdTokenPayload.sid) {
650
+ console.debug('SessionMonitor._callback: Same sub still logged in at OP, restarting check session iframe; session_state:', sessionState);
651
+ } else {
652
+ console.debug('SessionMonitor._callback: Same sub still logged in at OP, session state has changed, restarting check session iframe; session_state:', sessionState);
653
+ }
654
+ } else {
655
+ console.debug('SessionMonitor._callback: Different subject signed into OP:', iFrameIdTokenPayload.sub);
656
+ }
657
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
658
+ }).catch(async (e) => {
659
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
660
+ for (const [key, oidc] of Object.entries(oidcDatabase)) {
661
+ // @ts-ignore
662
+ await oidc.logoutOtherTabAsync(this.configuration.client_id, idTokenPayload.sub);
663
+ }
664
+ });
665
+ };
666
+
667
+ this.checkSessionIFrame = new CheckSessionIFrame(checkSessionCallback, clientId, checkSessionIFrameUri);
668
+ this.checkSessionIFrame.load().then(() => {
669
+ this.checkSessionIFrame.start(sessionState);
670
+ resolve();
671
+ }).catch((e) => {
672
+ reject(e);
673
+ });
674
+ } else {
675
+ resolve();
676
+ }
677
+ });
678
+ }
679
+
342
680
  loginCallbackPromise : Promise<any> = null;
343
681
  async loginCallbackAsync(isSilenSignin = false) {
344
682
  if (this.loginCallbackPromise !== null) {
@@ -351,9 +689,9 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
351
689
  const parsedTokens = response.tokens;
352
690
  // @ts-ignore
353
691
  this.tokens = response.tokens;
354
- const serviceWorker = await initWorkerAsync(this.configuration.service_worker_relative_url, this.configurationName);
692
+ const serviceWorker = await initWorkerAsync(this.configuration.service_worker_relative_url, this.configurationName, this.configuration.redirect_uri);
355
693
  if (!serviceWorker) {
356
- const session = initSession(this.configurationName, this.configuration.storage);
694
+ const session = initSession(this.configurationName, this.configuration.redirect_uri, this.configuration.storage);
357
695
  session.setTokens(parsedTokens);
358
696
  }
359
697
  this.publishEvent(Oidc.eventNames.token_aquired, parsedTokens);
@@ -378,10 +716,9 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
378
716
  const oidcServerConfiguration = await this.initAsync(authority, configuration.authority_configuration);
379
717
  const queryParams = getParseQueryStringFromLocation(window.location.href);
380
718
  const sessionState = queryParams.session_state;
381
- const serviceWorker = await initWorkerAsync(configuration.service_worker_relative_url, this.configurationName);
719
+ const serviceWorker = await initWorkerAsync(configuration.service_worker_relative_url, this.configurationName, configuration.redirect_uri);
382
720
  let storage = null;
383
721
  let nonceData = null;
384
- let getLoginParams = null;
385
722
  if (serviceWorker) {
386
723
  serviceWorker.startKeepAliveServiceWorker();
387
724
  await serviceWorker.initAsync(oidcServerConfiguration, 'loginCallbackAsync', configuration);
@@ -394,14 +731,12 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
394
731
  await storage.removeItem('dummy');
395
732
  await serviceWorker.setSessionStateAsync(sessionState);
396
733
  nonceData = await serviceWorker.getNonceAsync();
397
- getLoginParams = serviceWorker.getLoginParams(this.configurationName);
398
734
  } else {
399
- const session = initSession(this.configurationName);
735
+ const session = initSession(this.configurationName, redirectUri);
400
736
  session.setSessionState(sessionState);
401
737
  const items = await session.loadItemsAsync();
402
738
  storage = new MemoryStorageBackend(session.saveItemsAsync, items);
403
739
  nonceData = await session.getNonceAsync();
404
- getLoginParams = session.getLoginParams(this.configurationName);
405
740
  }
406
741
 
407
742
  return new Promise((resolve, reject) => {
@@ -426,28 +761,21 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
426
761
  return;
427
762
  }
428
763
 
429
- const extras = {};
764
+ let extras = null;
430
765
  if (request && request.internal) {
431
- // @ts-ignore
766
+ extras = {};
432
767
  extras.code_verifier = request.internal.code_verifier;
433
768
  if (configuration.token_request_extras) {
434
769
  for (const [key, value] of Object.entries(configuration.token_request_extras)) {
435
770
  extras[key] = value;
436
771
  }
437
772
  }
438
- if (getLoginParams && getLoginParams.extras) {
439
- for (const [key, value] of Object.entries(getLoginParams.extras)) {
440
- if (key.endsWith(':token_request')) {
441
- extras[key.replace(':token_request', '')] = value;
442
- }
443
- }
444
- }
445
773
  }
446
774
 
447
775
  const tokenRequest = new TokenRequest({
448
776
  client_id: clientId,
449
- redirect_uri: redirectUri, // @ts-ignore
450
- grant_type: extras.grant_type ?? GRANT_TYPE_AUTHORIZATION_CODE,
777
+ redirect_uri: redirectUri,
778
+ grant_type: GRANT_TYPE_AUTHORIZATION_CODE,
451
779
  code: response.code,
452
780
  refresh_token: undefined,
453
781
  extras,
@@ -467,11 +795,11 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
467
795
  let formattedTokens = null;
468
796
  if (serviceWorker) {
469
797
  const { tokens } = await serviceWorker.initAsync(oidcServerConfiguration, 'syncTokensAsync', configuration);
470
- loginParams = serviceWorker.getLoginParams(this.configurationName);
798
+ loginParams = serviceWorker.getLoginParams(this.configurationName, redirectUri);
471
799
  formattedTokens = tokens;
472
800
  } else {
473
- const session = initSession(this.configurationName, configuration.storage);
474
- loginParams = session.getLoginParams(this.configurationName);
801
+ const session = initSession(this.configurationName, redirectUri, configuration.storage);
802
+ loginParams = session.getLoginParams(this.configurationName, redirectUri);
475
803
  formattedTokens = setTokens(tokenResponse, null, configuration.token_renew_mode);
476
804
  }
477
805
  if (!isTokensOidcValid(formattedTokens, nonceData.nonce, oidcServerConfiguration)) {
@@ -540,12 +868,12 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
540
868
  const localsilentLoginAsync = async () => {
541
869
  try {
542
870
  let loginParams = null;
543
- const serviceWorker = await initWorkerAsync(configuration.service_worker_relative_url, this.configurationName);
871
+ const serviceWorker = await initWorkerAsync(configuration.service_worker_relative_url, this.configurationName, configuration.redirect_uri);
544
872
  if (serviceWorker) {
545
- loginParams = serviceWorker.getLoginParams(this.configurationName);
873
+ loginParams = serviceWorker.getLoginParams(this.configurationName, configuration.redirect_uri);
546
874
  } else {
547
- const session = initSession(this.configurationName, configuration.storage);
548
- loginParams = session.getLoginParams(this.configurationName);
875
+ const session = initSession(this.configurationName, configuration.redirect_uri, configuration.storage);
876
+ loginParams = session.getLoginParams(this.configurationName, configuration.redirect_uri);
549
877
  }
550
878
  const silent_token_response = await this.silentLoginAsync({
551
879
  ...loginParams.extras,
@@ -657,7 +985,7 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
657
985
  }
658
986
  let nonce = nullNonce;
659
987
  const oidcServerConfiguration = await this.initAsync(configuration.authority, configuration.authority_configuration);
660
- const serviceWorker = await initWorkerAsync(configuration.service_worker_relative_url, configurationName);
988
+ const serviceWorker = await initWorkerAsync(configuration.service_worker_relative_url, configurationName, configuration.redirect_uri);
661
989
  if (serviceWorker) {
662
990
  const { status, tokens } = await serviceWorker.initAsync(oidcServerConfiguration, 'syncTokensAsync', configuration);
663
991
  if (status === 'LOGGED_OUT') {
@@ -674,7 +1002,7 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
674
1002
  }
675
1003
  nonce = await serviceWorker.getNonceAsync();
676
1004
  } else {
677
- const session = initSession(configurationName, configuration.storage ?? sessionStorage);
1005
+ const session = initSession(configurationName, configuration.redirect_uri, configuration.storage ?? sessionStorage);
678
1006
  const { tokens, status } = await session.initAsync();
679
1007
  if (!tokens) {
680
1008
  return { tokens: null, status: 'LOGOUT_FROM_ANOTHER_TAB', nonce: nullNonce };
@@ -736,9 +1064,9 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
736
1064
  if (this.checkSessionIFrame) {
737
1065
  this.checkSessionIFrame.stop();
738
1066
  }
739
- const serviceWorker = await initWorkerAsync(this.configuration.service_worker_relative_url, this.configurationName);
1067
+ const serviceWorker = await initWorkerAsync(this.configuration.service_worker_relative_url, this.configurationName, this.configuration.redirect_uri);
740
1068
  if (!serviceWorker) {
741
- const session = initSession(this.configurationName, this.configuration.storage);
1069
+ const session = initSession(this.configurationName, this.configuration.redirect_uri, this.configuration.storage);
742
1070
  await session.clearAsync(status);
743
1071
  } else {
744
1072
  await serviceWorker.clearAsync(status);
@@ -1,29 +1,5 @@
1
- import { getFromCache, setCache } from './cache';
2
- import { OidcAuthorizationServiceConfiguration } from './oidc';
3
1
  import { parseOriginalTokens } from './parseTokens';
4
2
 
5
- const oneHourSecond = 60 * 60;
6
- export const fetchFromIssuer = async (openIdIssuerUrl: string, timeCacheSecond = oneHourSecond, storage = window.sessionStorage):
7
- Promise<OidcAuthorizationServiceConfiguration> => {
8
- const fullUrl = `${openIdIssuerUrl}/.well-known/openid-configuration`;
9
-
10
- const localStorageKey = `oidc.server:${openIdIssuerUrl}`;
11
- const data = getFromCache(localStorageKey, storage, timeCacheSecond);
12
- if (data) {
13
- return new OidcAuthorizationServiceConfiguration(data);
14
- }
15
- const response = await fetch(fullUrl);
16
-
17
- if (response.status !== 200) {
18
- return null;
19
- }
20
-
21
- const result = await response.json();
22
-
23
- setCache(localStorageKey, result, storage);
24
- return new OidcAuthorizationServiceConfiguration(result);
25
- };
26
-
27
3
  const internalFetch = async (url, headers, numberRetry = 0, timeoutMs = 10000) => {
28
4
  let response;
29
5
  try {
@@ -72,7 +72,7 @@ const parseQueryString = (queryString:string) => {
72
72
  // Convert the array of strings into an object
73
73
  for (i = 0, l = queries.length; i < l; i++) {
74
74
  temp = queries[i].split('=');
75
- params[decodeURIComponent(temp[0])] = temp[1];
75
+ params[temp[0]] = temp[1];
76
76
  }
77
77
 
78
78
  return params;
@@ -1,6 +1,5 @@
1
- import { LoginCallback, Oidc } from './oidc';
1
+ import { LoginCallback, Oidc, OidcConfiguration, StringMap } from './oidc';
2
2
  import { getValidTokenAsync, Tokens, ValidToken } from './parseTokens';
3
- import { OidcConfiguration, StringMap } from './types';
4
3
 
5
4
  export interface EventSubscriber {
6
5
  (name: string, data:any);