@axa-fr/react-oidc 6.0.0-beta7 → 6.0.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 (50) hide show
  1. package/README.md +13 -4
  2. package/dist/FetchToken.d.ts.map +1 -1
  3. package/dist/FetchToken.js +10 -6
  4. package/dist/FetchToken.js.map +1 -1
  5. package/dist/OidcProvider.d.ts +1 -0
  6. package/dist/OidcProvider.d.ts.map +1 -1
  7. package/dist/OidcProvider.js +11 -4
  8. package/dist/OidcProvider.js.map +1 -1
  9. package/dist/OidcSecure.js +2 -2
  10. package/dist/OidcSecure.js.map +1 -1
  11. package/dist/OidcServiceWorker.js +62 -32
  12. package/dist/OidcTrustedDomains.js +7 -2
  13. package/dist/ReactOidc.d.ts.map +1 -1
  14. package/dist/ReactOidc.js +4 -3
  15. package/dist/ReactOidc.js.map +1 -1
  16. package/dist/core/default-component/SilentLogin.component.js +1 -1
  17. package/dist/core/default-component/SilentLogin.component.js.map +1 -1
  18. package/dist/core/routes/OidcRoutes.d.ts.map +1 -1
  19. package/dist/core/routes/OidcRoutes.js +1 -4
  20. package/dist/core/routes/OidcRoutes.js.map +1 -1
  21. package/dist/vanilla/initSession.d.ts +2 -1
  22. package/dist/vanilla/initSession.d.ts.map +1 -1
  23. package/dist/vanilla/initSession.js +7 -7
  24. package/dist/vanilla/initSession.js.map +1 -1
  25. package/dist/vanilla/initWorker.d.ts +2 -3
  26. package/dist/vanilla/initWorker.d.ts.map +1 -1
  27. package/dist/vanilla/initWorker.js +6 -21
  28. package/dist/vanilla/initWorker.js.map +1 -1
  29. package/dist/vanilla/oidc.d.ts +10 -5
  30. package/dist/vanilla/oidc.d.ts.map +1 -1
  31. package/dist/vanilla/oidc.js +451 -442
  32. package/dist/vanilla/oidc.js.map +1 -1
  33. package/package.json +1 -1
  34. package/src/oidc/FetchToken.tsx +7 -4
  35. package/src/oidc/OidcProvider.tsx +9 -0
  36. package/src/oidc/OidcSecure.tsx +2 -2
  37. package/src/oidc/ReactOidc.tsx +4 -3
  38. package/src/oidc/core/default-component/SilentLogin.component.tsx +1 -1
  39. package/src/oidc/core/routes/OidcRoutes.tsx +0 -4
  40. package/src/oidc/vanilla/OidcServiceWorker.js +62 -32
  41. package/src/oidc/vanilla/OidcTrustedDomains.js +7 -2
  42. package/src/oidc/vanilla/initSession.ts +6 -7
  43. package/src/oidc/vanilla/initWorker.ts +6 -15
  44. package/src/oidc/vanilla/oidc.ts +219 -241
  45. package/src/oidc/vanilla/parseTokens.js +107 -0
  46. package/dist/core/default-component/ServiceWorkerInstall.component.d.ts +0 -4
  47. package/dist/core/default-component/ServiceWorkerInstall.component.d.ts.map +0 -1
  48. package/dist/core/default-component/ServiceWorkerInstall.component.js +0 -131
  49. package/dist/core/default-component/ServiceWorkerInstall.component.js.map +0 -1
  50. package/src/oidc/core/default-component/ServiceWorkerInstall.component.tsx +0 -60
@@ -19,6 +19,7 @@ import timer from './timer';
19
19
  import {CheckSessionIFrame} from "./checkSessionIFrame"
20
20
  import {getParseQueryStringFromLocation} from "./route-utils";
21
21
  import {AuthorizationServiceConfigurationJson} from "@openid/appauth/src/authorization_service_configuration";
22
+ import {computeTimeLeft, isTokensValid, parseOriginalTokens, setTokens} from "./parseTokens";
22
23
 
23
24
  const performTokenRequestAsync= async (url, details, extras) => {
24
25
 
@@ -47,22 +48,9 @@ const performTokenRequestAsync= async (url, details, extras) => {
47
48
  return {success:false, status: response.status}
48
49
  }
49
50
  const tokens = await response.json();
50
-
51
- if(!tokens.issued_at) {
52
- const currentTimeUnixSecond = new Date().getTime() /1000;
53
- tokens.issued_at = currentTimeUnixSecond;
54
- }
55
-
56
- return { success : true,
57
- data : {
58
- accessToken: tokens.access_token,
59
- expiresIn: tokens.expires_in,
60
- idToken: tokens.id_token,
61
- refreshToken: tokens.refresh_token,
62
- scope: tokens.scope,
63
- tokenType: tokens.token_type,
64
- issuedAt: tokens.issued_at
65
- }
51
+ return {
52
+ success : true,
53
+ data: parseOriginalTokens(tokens)
66
54
  };
67
55
  }
68
56
 
@@ -82,7 +70,6 @@ const internalFetch = async (url, headers, numberRetry=0) => {
82
70
  throw e;
83
71
  }
84
72
  } else {
85
-
86
73
  console.error(e.message);
87
74
  throw e; // rethrow other unexpected errors
88
75
  }
@@ -108,36 +95,6 @@ export class OidcAuthorizationServiceConfiguration extends AuthorizationServiceC
108
95
 
109
96
  }
110
97
 
111
- const idTokenPayload = (token) => {
112
- const base64Url = token.split('.')[1];
113
- const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
114
- const jsonPayload = decodeURIComponent(atob(base64).split('').map(function (c) {
115
- return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
116
- }).join(''));
117
-
118
- return JSON.parse(jsonPayload);
119
- }
120
-
121
- const countLetter = (str, find)=> {
122
- return (str.split(find)).length - 1;
123
- }
124
-
125
- const extractAccessTokenPayload = tokens => {
126
- if(tokens.accessTokenPayload)
127
- {
128
- return tokens.accessTokenPayload;
129
- }
130
- const accessToken = tokens.accessToken;
131
- try{
132
- if (!accessToken || countLetter(accessToken,'.') != 2) {
133
- return null;
134
- }
135
- return JSON.parse(atob(accessToken.split('.')[1]));
136
- } catch (e) {
137
- console.warn(e);
138
- }
139
- return null;
140
- };
141
98
 
142
99
  export interface StringMap {
143
100
  [key: string]: string;
@@ -193,44 +150,46 @@ const loginCallbackWithAutoTokensRenewAsync = async (oidc) => {
193
150
  }
194
151
 
195
152
  const autoRenewTokens = (oidc, refreshToken, expiresAt) => {
196
- const refreshTimeBeforeTokensExpirationInSecond = oidc.configuration.refresh_time_before_tokens_expiration_in_second ?? 60;
153
+ const refreshTimeBeforeTokensExpirationInSecond = oidc.configuration.refresh_time_before_tokens_expiration_in_second;
197
154
  return timer.setTimeout(async () => {
198
- const currentTimeUnixSecond = new Date().getTime() /1000;
199
- const timeInfo = { timeLeft: Math.round(((expiresAt - refreshTimeBeforeTokensExpirationInSecond) - currentTimeUnixSecond))};
155
+ const timeLeft = computeTimeLeft(refreshTimeBeforeTokensExpirationInSecond, expiresAt);
156
+ const timeInfo = { timeLeft };
200
157
  oidc.publishEvent(Oidc.eventNames.token_timer, timeInfo);
201
- if(currentTimeUnixSecond > (expiresAt - refreshTimeBeforeTokensExpirationInSecond)) {
202
- const tokens = await oidc.refreshTokensAsync(refreshToken);
203
- oidc.tokens= await setTokensAsync(oidc.serviceWorker, tokens);
158
+ const {tokens, status} = await oidc.synchroniseTokensAsync(refreshToken);
159
+ oidc.tokens= tokens;
204
160
  if(!oidc.serviceWorker){
205
161
  await oidc.session.setTokens(oidc.tokens);
206
162
  }
207
163
  if(!oidc.tokens){
208
- if(oidc.checkSessionIFrame){
209
- oidc.checkSessionIFrame.stop();
210
- oidc.checkSessionIFrame = null;
211
- }
164
+ await oidc.destroyAsync(status);
212
165
  return;
213
166
  }
214
- oidc.publishEvent(Oidc.eventNames.token_renewed, {});
167
+
215
168
  if(oidc.timeoutId) {
216
169
  oidc.timeoutId = autoRenewTokens(oidc, tokens.refreshToken, oidc.tokens.expiresAt);
217
170
  }
218
- } else{
219
- await oidc.syncTokensAsync();
220
- if(oidc.timeoutId) {
221
- oidc.timeoutId = autoRenewTokens(oidc, refreshToken, expiresAt);
222
- }
223
- }
171
+
224
172
  }, 1000);
225
173
  }
226
174
 
227
175
  const getLoginSessionKey = (configurationName:string, redirectUri:string) => {
228
176
  return `oidc_login.${configurationName}:${redirectUri}`;
229
177
  }
230
- const getLoginParams = (configurationName, redirectUri) => {
231
- return JSON.parse(sessionStorage[getLoginSessionKey(configurationName, redirectUri)]);
178
+
179
+ const setLoginParams = (configurationName:string, redirectUri:string, data) =>{
180
+ const sessionKey = getLoginSessionKey(configurationName, redirectUri);
181
+ getLoginParamsCache = data
182
+ sessionStorage[sessionKey] = JSON.stringify(data);
232
183
  }
233
184
 
185
+ let getLoginParamsCache = null;
186
+ const getLoginParams = (configurationName, redirectUri) => {
187
+ const dataString = sessionStorage[getLoginSessionKey(configurationName, redirectUri)];
188
+ if(!getLoginParamsCache){
189
+ getLoginParamsCache = JSON.parse(dataString);
190
+ }
191
+ return getLoginParamsCache;
192
+ }
234
193
 
235
194
  const userInfoAsync = async (oidc) => {
236
195
  if(oidc.userInfo != null){
@@ -239,11 +198,15 @@ const userInfoAsync = async (oidc) => {
239
198
  if(!oidc.tokens){
240
199
  return null;
241
200
  }
242
- if(oidc.syncTokensAsyncPromise){
243
- await oidc.syncTokensAsyncPromise;
244
- }
245
201
  const accessToken = oidc.tokens.accessToken;
246
-
202
+ if(!accessToken){
203
+ return null;
204
+ }
205
+ // We wait the synchronisation before making a request
206
+ while (oidc.tokens && !isTokensValid(oidc.tokens)){
207
+ await sleepAsync(200);
208
+ }
209
+
247
210
  const oidcServerConfiguration = await oidc.initAsync(oidc.configuration.authority, oidc.configuration.authority_configuration);
248
211
  const url = oidcServerConfiguration.userInfoEndpoint;
249
212
  const fetchUserInfo = async (accessToken) => {
@@ -265,29 +228,11 @@ const userInfoAsync = async (oidc) => {
265
228
  return userInfo;
266
229
  }
267
230
 
268
- const setTokensAsync = async (serviceWorker, tokens) =>{
269
- let accessTokenPayload;
270
- if(tokens == null){
271
- if(serviceWorker){
272
- await serviceWorker.clearAsync();
273
- }
274
- return null;
275
- }
276
- if(serviceWorker){
277
- accessTokenPayload = await serviceWorker.getAccessTokenPayloadAsync();
278
- }
279
- else {
280
- accessTokenPayload = extractAccessTokenPayload(tokens);
281
- }
282
- const _idTokenPayload = idTokenPayload(tokens.idToken);
283
- const expiresAt = (_idTokenPayload && _idTokenPayload.exp) ? _idTokenPayload.exp : tokens.issuedAt + tokens.expiresIn;
284
- return {...tokens, idTokenPayload: _idTokenPayload, accessTokenPayload, expiresAt};
285
- }
286
-
287
231
  const eventNames = {
288
232
  service_worker_not_supported_by_browser: "service_worker_not_supported_by_browser",
289
233
  token_aquired: "token_aquired",
290
234
  logout_from_another_tab: "logout_from_another_tab",
235
+ logout_from_same_tab: "logout_from_same_tab",
291
236
  token_renewed: "token_renewed",
292
237
  token_timer: "token_timer",
293
238
  loginAsync_begin:"loginAsync_begin",
@@ -380,7 +325,12 @@ export class Oidc {
380
325
  silent_login_uri = `${configuration.silent_redirect_uri.replace("-callback", "").replace("callback", "")}-login`;
381
326
  }
382
327
 
383
- this.configuration = {...configuration, silent_login_uri};
328
+ this.configuration = {...configuration,
329
+ silent_login_uri,
330
+ monitor_session: configuration.monitor_session ?? true,
331
+ refresh_time_before_tokens_expiration_in_second : configuration.refresh_time_before_tokens_expiration_in_second ?? 60,
332
+ silent_login_timeout: configuration.silent_login_timeout ?? 12000,
333
+ };
384
334
  this.configurationName= configurationName;
385
335
  this.tokens = null
386
336
  this.userInfo = null;
@@ -388,7 +338,7 @@ export class Oidc {
388
338
  this.timeoutId = null;
389
339
  this.serviceWorker = null;
390
340
  this.session = null;
391
- this.refreshTokensAsync.bind(this);
341
+ this.synchroniseTokensAsync.bind(this);
392
342
  this.loginCallbackWithAutoTokensRenewAsync.bind(this);
393
343
  this.initAsync.bind(this);
394
344
  this.loginCallbackAsync.bind(this);
@@ -458,10 +408,6 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
458
408
  if (!this.configuration.silent_redirect_uri || !this.configuration.silent_login_uri) {
459
409
  return Promise.resolve(null);
460
410
  }
461
- while (document.hidden) {
462
- await sleepAsync(1000);
463
- this.publishEvent(eventNames.silentLoginAsync, {message:"wait because document is hidden"});
464
- }
465
411
 
466
412
  try {
467
413
  this.publishEvent(eventNames.silentLoginAsync_begin, {});
@@ -526,19 +472,19 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
526
472
  self.publishEvent(eventNames.silentLoginAsync_error, result);
527
473
  iframe.remove();
528
474
  isResolved = true;
529
- reject(result);
475
+ reject(new Error("oidc_"+result.error));
530
476
  }
531
477
  }
532
478
  }
533
479
  }
534
480
  };
535
- const silentSigninTimeout = configuration.silent_login_timeout ?? 12000
481
+ const silentSigninTimeout = configuration.silent_login_timeout;
536
482
  setTimeout(() => {
537
483
  if (!isResolved) {
538
- self.publishEvent(eventNames.silentLoginAsync_error, "timeout");
484
+ self.publishEvent(eventNames.silentLoginAsync_error, {reason: "timeout"});
539
485
  iframe.remove();
540
486
  isResolved = true;
541
- reject("timeout");
487
+ reject(new Error("timeout"));
542
488
  }
543
489
  }, silentSigninTimeout);
544
490
  } catch (e) {
@@ -566,8 +512,7 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
566
512
 
567
513
  const serviceWorker = await initWorkerAsync(this.configuration.service_worker_relative_url, this.configurationName);
568
514
  const storage = serviceWorker ? window.localStorage : null;
569
- const initAsyncPromise = await fetchFromIssuer(authority, this.configuration.authority_time_cache_wellknowurl_in_second ?? 60 * 60, storage);
570
- return initAsyncPromise;
515
+ return await fetchFromIssuer(authority, this.configuration.authority_time_cache_wellknowurl_in_second ?? 60 * 60, storage);
571
516
  }
572
517
 
573
518
  tryKeepExistingSessionPromise = null;
@@ -591,16 +536,7 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
591
536
  if (tokens) {
592
537
  serviceWorker.startKeepAliveServiceWorker();
593
538
  // @ts-ignore
594
- const reformattedToken = {
595
- accessToken : tokens.access_token,
596
- refreshToken : tokens.refresh_token,
597
- expiresIn: tokens.expires_in,
598
- idToken: tokens.id_token,
599
- scope: tokens.scope,
600
- tokenType: tokens.token_type,
601
- issuedAt: tokens.issued_at
602
- }
603
- this.tokens = await setTokensAsync(serviceWorker, reformattedToken);
539
+ this.tokens = tokens;
604
540
  this.serviceWorker = serviceWorker;
605
541
  // @ts-ignore
606
542
  this.timeoutId = autoRenewTokens(this, this.tokens.refreshToken, this.tokens.expiresAt);
@@ -627,7 +563,7 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
627
563
  const {tokens} = await session.initAsync();
628
564
  if (tokens) {
629
565
  // @ts-ignore
630
- this.tokens = await setTokensAsync(serviceWorker, tokens);
566
+ this.tokens = setTokens(tokens);
631
567
  //session.setTokens(this.tokens);
632
568
  this.session = session;
633
569
  // @ts-ignore
@@ -665,7 +601,7 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
665
601
  }
666
602
 
667
603
  loginPromise: Promise<any>=null;
668
- async loginAsync(callbackPath:string=undefined, extras:StringMap=null, installServiceWorker=true, state:string=undefined, isSilentSignin:boolean=false, scope:string=undefined) {
604
+ async loginAsync(callbackPath:string=undefined, extras:StringMap=null, state:string=undefined, isSilentSignin:boolean=false, scope:string=undefined) {
669
605
  if(this.loginPromise !== null){
670
606
  return this.loginPromise;
671
607
  }
@@ -682,28 +618,10 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
682
618
  scope = configuration.scope;
683
619
  }
684
620
 
685
- const sessionKey = getLoginSessionKey(this.configurationName, redirectUri);
686
- sessionStorage[sessionKey] = JSON.stringify({callbackPath: url, extras, state});
687
-
621
+ setLoginParams(this.configurationName, redirectUri, {callbackPath: url, extras, state});
622
+
688
623
  let serviceWorker = await initWorkerAsync(configuration.service_worker_relative_url, this.configurationName);
689
624
  const oidcServerConfiguration = await this.initAsync(configuration.authority, configuration.authority_configuration);
690
- /*if (serviceWorker && installServiceWorker) {
691
- const isServiceWorkerProxyActive = await serviceWorker.isServiceWorkerProxyActiveAsync();
692
- if (!isServiceWorkerProxyActive) {
693
- const isUnregistered = await serviceWorker.unregisterAsync();
694
- console.log("isUnregistered")
695
- console.log(isUnregistered)
696
- if(isUnregistered){
697
- serviceWorker = await initWorkerAsync(configuration.service_worker_relative_url, this.configurationName);
698
- }
699
- const extrasQueries = extras != null ? {...extras}: {};
700
- extrasQueries.callbackPath = url;
701
- extrasQueries.state = state;
702
- const queryString = buildQueries(extrasQueries);
703
- window.location.href = `${redirectUri}/service-worker-install${queryString}`;
704
- //return;
705
- }
706
- }*/
707
625
  let storage;
708
626
  if (serviceWorker) {
709
627
  serviceWorker.startKeepAliveServiceWorker();
@@ -772,9 +690,16 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
772
690
  else {
773
691
  console.debug("SessionMonitor._callback: Different subject signed into OP:", iFrameIdTokenPayload.sub);
774
692
  }
775
- }).catch((e) => {
776
- this.publishEvent(eventNames.logout_from_another_tab, {});
777
- this.destroyAsync();
693
+ }).catch(async (e) => {
694
+ for (const [key, oidc] of Object.entries(oidcDatabase)) {
695
+ //if(oidc !== this) {
696
+ // @ts-ignore
697
+ await oidc.logoutOtherTabAsync(this.configuration.client_id, idTokenPayload.sub);
698
+ //}
699
+ }
700
+ //await this.destroyAsync();
701
+ //this.publishEvent(eventNames.logout_from_another_tab, {message : "SessionMonitor"});
702
+
778
703
  });
779
704
  };
780
705
 
@@ -801,7 +726,7 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
801
726
  const response = await this._loginCallbackAsync(isSilenSignin);
802
727
  // @ts-ignore
803
728
  const tokens = response.tokens;
804
- const parsedTokens = await setTokensAsync(this.serviceWorker, tokens);
729
+ const parsedTokens = setTokens(tokens);
805
730
  this.tokens = parsedTokens;
806
731
  if(!this.serviceWorker){
807
732
  await this.session.setTokens(parsedTokens);
@@ -901,13 +826,19 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
901
826
  }, tokenRequestTimeout ?? 12000);
902
827
  try {
903
828
  const tokenHandler = new BaseTokenRequestHandler(new FetchRequestor());
904
- tokenHandler.performTokenRequest(oidcServerConfiguration, tokenRequest).then((tokenResponse)=>{
905
- if(timeoutId) {
829
+ tokenHandler.performTokenRequest(oidcServerConfiguration, tokenRequest).then(async (tokenResponse) => {
830
+ if (timeoutId) {
906
831
  clearTimeout(timeoutId);
907
- this.timeoutId=null;
832
+ this.timeoutId = null;
908
833
  const loginParams = getLoginParams(this.configurationName, redirectUri);
834
+
835
+ if (serviceWorker) {
836
+ const {tokens} = await serviceWorker.initAsync(oidcServerConfiguration, "syncTokensAsync");
837
+ tokenResponse = tokens;
838
+ }
839
+
909
840
  // @ts-ignore
910
- this.startCheckSessionAsync(oidcServerConfiguration.check_session_iframe, clientId, sessionState, isSilentSignin).then(() =>{
841
+ this.startCheckSessionAsync(oidcServerConfiguration.check_session_iframe, clientId, sessionState, isSilentSignin).then(() => {
911
842
  this.publishEvent(eventNames.loginCallbackAsync_end, {});
912
843
  resolve({
913
844
  tokens: tokenResponse,
@@ -936,130 +867,153 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
936
867
  }
937
868
  }
938
869
 
939
- async refreshTokensAsync(refreshToken) {
870
+ async synchroniseTokensAsync(refreshToken, index=0) {
940
871
 
872
+ if (document.hidden) {
873
+ await sleepAsync(1000);
874
+ this.publishEvent(eventNames.refreshTokensAsync, {message: "wait because document is hidden"});
875
+ return await this.synchroniseTokensAsync(refreshToken, index);
876
+ }
877
+ let numberTryOnline = 6;
878
+ while (!navigator.onLine && numberTryOnline > 0) {
879
+ await sleepAsync(1000);
880
+ numberTryOnline--;
881
+ this.publishEvent(eventNames.refreshTokensAsync, {message: `wait because navigator is offline try ${numberTryOnline}` });
882
+ }
883
+
884
+ const configuration = this.configuration;
941
885
  const localsilentLoginAsync= async () => {
942
886
  try {
943
- const silent_token_response = await this.silentLoginAsync();
887
+ const loginParams = getLoginParams(this.configurationName, configuration.redirect_uri);
888
+ const silent_token_response = await this.silentLoginAsync({
889
+ ...loginParams.extras,
890
+ prompt: "none"
891
+ }, loginParams.state);
944
892
  if (silent_token_response) {
945
- return silent_token_response.tokens;
893
+ this.publishEvent(Oidc.eventNames.token_renewed, {});
894
+ return {tokens:silent_token_response.tokens, status:"LOGGED"};
946
895
  }
947
896
  } catch (exceptionSilent) {
948
897
  console.error(exceptionSilent);
898
+ this.publishEvent(eventNames.refreshTokensAsync_silent_error, {message: "exceptionSilent" ,exception: exceptionSilent.message});
899
+ if(exceptionSilent && exceptionSilent.message && exceptionSilent.message.startsWith("oidc")){
900
+ this.publishEvent(eventNames.refreshTokensAsync_error, {message: `refresh token silent` });
901
+ return {tokens:null, status:"SESSION_LOST"};
902
+ }
903
+ await sleepAsync(1000);
904
+ throw exceptionSilent;
949
905
  }
950
- if(this.timeoutId){
951
- timer.clearTimeout(this.timeoutId);
952
- this.timeoutId=null;
953
- }
954
- this.publishEvent(eventNames.refreshTokensAsync_error, {message: "refresh token and silent refresh failed"});
955
- return null;
906
+ this.publishEvent(eventNames.refreshTokensAsync_error, {message: `refresh token silent return` });
907
+ return {tokens:null, status:"SESSION_LOST"};
956
908
  }
957
-
958
- const configuration = this.configuration;
959
- const clientId = configuration.client_id;
960
- const redirectUri = configuration.redirect_uri;
961
- const authority = configuration.authority;
962
-
963
- if(!refreshToken)
964
- {
965
- return await localsilentLoginAsync();
966
- }
967
-
968
- let extras = {};
969
- if(configuration.token_request_extras) {
970
- for (let [key, value] of Object.entries(configuration.token_request_extras)) {
971
- extras[key] = value;
972
- }
973
- }
974
- const oidcServerConfiguration = await this.initAsync(authority, configuration.authority_configuration);
975
-
976
- const details = {
977
- client_id: clientId,
978
- redirect_uri: redirectUri,
979
- grant_type: GRANT_TYPE_REFRESH_TOKEN,
980
- refresh_token: refreshToken,
981
- };
982
909
 
983
- let index = 0;
984
- while (index <=2) {
910
+ if (index <=4) {
985
911
  try {
986
- this.publishEvent(eventNames.refreshTokensAsync_begin, {refreshToken:refreshToken, tryNumber: index});
987
- if(index > 1) {
988
- while (document.hidden) {
989
- await sleepAsync(1000);
990
- this.publishEvent(eventNames.refreshTokensAsync, {message: "wait because document is hidden"});
991
- }
992
- }
993
- const tokenResponse = await performTokenRequestAsync(oidcServerConfiguration.tokenEndpoint, details, extras)
994
- if (tokenResponse.success) {
995
- this.publishEvent(eventNames.refreshTokensAsync_end, {success: tokenResponse.success});
996
- return tokenResponse.data;
997
- } else {
998
- this.publishEvent(eventNames.refreshTokensAsync_silent_error, {message: "bad request" , tokenResponse: tokenResponse});
912
+
913
+ if(!refreshToken)
914
+ {
915
+ this.publishEvent(eventNames.refreshTokensAsync_begin, {refreshToken:refreshToken, tryNumber: index});
999
916
  return await localsilentLoginAsync();
1000
917
  }
918
+ const { status, tokens } = await this.syncTokensInfoAsync(configuration, this.configurationName, this.tokens);
919
+ switch (status) {
920
+ case "SESSION_LOST":
921
+ this.publishEvent(eventNames.refreshTokensAsync_error, {message: `refresh token session lost` });
922
+ return {tokens:null, status:"SESSION_LOST"};
923
+ case "NOT_CONNECTED":
924
+ return {tokens:null, status:null};
925
+ case "TOKENS_VALID":
926
+ case "TOKEN_UPDATED_BY_ANOTHER_TAB_TOKENS_VALID":
927
+ return {tokens, status:"LOGGED_IN"};
928
+ case "LOGOUT_FROM_ANOTHER_TAB":
929
+ this.publishEvent(eventNames.logout_from_another_tab, {"status": "session syncTokensAsync"});
930
+ return {tokens:null, status:"LOGGED_OUT"};
931
+ case "REQUIRE_SYNC_TOKENS":
932
+ this.publishEvent(eventNames.refreshTokensAsync_begin, {refreshToken:refreshToken, status, tryNumber: index});
933
+ return await localsilentLoginAsync();
934
+ default:
935
+ this.publishEvent(eventNames.refreshTokensAsync_begin, {refreshToken:refreshToken, status, tryNumber: index});
936
+ const clientId = configuration.client_id;
937
+ const redirectUri = configuration.redirect_uri;
938
+ const authority = configuration.authority;
939
+ let extras = {};
940
+ if(configuration.token_request_extras) {
941
+ for (let [key, value] of Object.entries(configuration.token_request_extras)) {
942
+ extras[key] = value;
943
+ }
944
+ }
945
+ const details = {
946
+ client_id: clientId,
947
+ redirect_uri: redirectUri,
948
+ grant_type: GRANT_TYPE_REFRESH_TOKEN,
949
+ refresh_token: tokens.refreshToken,
950
+ };
951
+ const oidcServerConfiguration = await this.initAsync(authority, configuration.authority_configuration);
952
+ const tokenResponse = await performTokenRequestAsync(oidcServerConfiguration.tokenEndpoint, details, extras)
953
+ if (tokenResponse.success) {
954
+ this.publishEvent(eventNames.refreshTokensAsync_end, {success: tokenResponse.success});
955
+ this.publishEvent(Oidc.eventNames.token_renewed, {});
956
+ return {tokens: tokenResponse.data, status:"LOGGED_IN"};
957
+ } else {
958
+ this.publishEvent(eventNames.refreshTokensAsync_silent_error, {
959
+ message: "bad request",
960
+ tokenResponse: tokenResponse
961
+ });
962
+ return await this.synchroniseTokensAsync(null, index+1);
963
+ }
964
+ }
1001
965
  } catch (exception) {
1002
966
  console.error(exception);
1003
967
  this.publishEvent(eventNames.refreshTokensAsync_silent_error, {message: "exception" ,exception: exception.message});
968
+ return this.synchroniseTokensAsync(refreshToken, index+1);
1004
969
  }
1005
- index++;
1006
970
  }
1007
-
971
+
972
+ this.publishEvent(eventNames.refreshTokensAsync_error, {message: `refresh token` });
973
+ return {tokens:null, status:"SESSION_LOST"};
1008
974
  }
1009
975
 
1010
- syncTokensAsyncPromise=null;
1011
- async syncTokensAsync() {
976
+ async syncTokensInfoAsync(configuration, configurationName, currentTokens) {
1012
977
  // Service Worker can be killed by the browser (when it wants,for example after 10 seconds of inactivity, so we retreieve the session if it happen)
1013
- const configuration = this.configuration;
1014
- if(!this.tokens){
1015
- return;
978
+ //const configuration = this.configuration;
979
+ if (!currentTokens) {
980
+ return { tokens : null, status: "NOT_CONNECTED"};
1016
981
  }
1017
982
 
1018
983
  const oidcServerConfiguration = await this.initAsync(configuration.authority, configuration.authority_configuration);
1019
- const serviceWorker = await initWorkerAsync(configuration.service_worker_relative_url, this.configurationName);
984
+ const serviceWorker = await initWorkerAsync(configuration.service_worker_relative_url, configurationName);
1020
985
  if (serviceWorker) {
1021
- const { isLogin } = await serviceWorker.initAsync(oidcServerConfiguration, "syncTokensAsync");
1022
- if(isLogin == false){
1023
- this.publishEvent(eventNames.logout_from_another_tab, {});
1024
- await this.destroyAsync();
1025
- }
1026
- else if (isLogin == null){
1027
- try {
1028
- this.publishEvent(eventNames.syncTokensAsync_begin, {});
1029
- this.syncTokensAsyncPromise = this.silentLoginAsync({prompt:"none"});
1030
- const silent_token_response = await this.syncTokensAsyncPromise;
1031
- if (silent_token_response && silent_token_response.tokens) {
1032
- this.tokens = await setTokensAsync(serviceWorker, silent_token_response.tokens);
1033
- } else{
1034
- this.publishEvent(eventNames.syncTokensAsync_error, {message:"no token found in result"});
1035
- if(this.timeoutId){
1036
- timer.clearTimeout(this.timeoutId);
1037
- this.timeoutId=null;
1038
- }
1039
- return;
1040
- }
1041
- } catch (exceptionSilent) {
1042
- console.error(exceptionSilent);
1043
- this.publishEvent(eventNames.syncTokensAsync_error, exceptionSilent);
1044
- if(this.timeoutId){
1045
- timer.clearTimeout(this.timeoutId);
1046
- this.timeoutId=null;
1047
- }
1048
- return;
1049
- }
1050
- this.syncTokensAsyncPromise = null;
1051
- this.publishEvent(eventNames.syncTokensAsync_end, {});
986
+ const {status, tokens} = await serviceWorker.initAsync(oidcServerConfiguration, "syncTokensAsync");
987
+ if (status == "LOGGED_OUT") {
988
+ return {tokens: null, status: "LOGOUT_FROM_ANOTHER_TAB"};
989
+ }else if (status == "SESSIONS_LOST") {
990
+ return { tokens : null, status: "SESSIONS_LOST"};
991
+ } else if (!status || !tokens) {
992
+ return { tokens : null, status: "REQUIRE_SYNC_TOKENS"};
993
+ } else if(tokens.issuedAt !== currentTokens.issuedAt) {
994
+ const timeLeft = computeTimeLeft(configuration.refresh_time_before_tokens_expiration_in_second, tokens.expiresAt);
995
+ const status = (timeLeft > 0) ? "TOKEN_UPDATED_BY_ANOTHER_TAB_TOKENS_VALID" : "TOKEN_UPDATED_BY_ANOTHER_TAB_TOKENS_INVALID";
996
+ return { tokens : tokens, status };
1052
997
  }
1053
998
  } else {
1054
- const session = initSession(this.configurationName, configuration.redirect_uri, configuration.storage ?? sessionStorage);
1055
- const {tokens} = await session.initAsync();
1056
- if(!tokens){
1057
- this.publishEvent(eventNames.logout_from_another_tab, {});
1058
- await this.destroyAsync();
999
+ const session = initSession(configurationName, configuration.redirect_uri, configuration.storage ?? sessionStorage);
1000
+ const {tokens, status } = await session.initAsync();
1001
+ if (!tokens) {
1002
+ return {tokens: null, status: "LOGOUT_FROM_ANOTHER_TAB"};
1003
+ } else if (status == "SESSIONS_LOST") {
1004
+ return { tokens : null, status: "SESSIONS_LOST"};
1005
+ }
1006
+ else if(tokens.issuedAt !== currentTokens.issuedAt){
1007
+ const timeLeft = computeTimeLeft(configuration.refresh_time_before_tokens_expiration_in_second, tokens.expiresAt);
1008
+ const status = (timeLeft > 0) ? "TOKEN_UPDATED_BY_ANOTHER_TAB_TOKENS_VALID" : "TOKEN_UPDATED_BY_ANOTHER_TAB_TOKENS_INVALID";
1009
+ return { tokens : tokens, status };
1059
1010
  }
1060
1011
  }
1061
- }
1062
1012
 
1013
+ const timeLeft = computeTimeLeft(configuration.refresh_time_before_tokens_expiration_in_second, currentTokens.expiresAt);
1014
+ const status = (timeLeft > 0) ? "TOKENS_VALID" : "TOKENS_INVALID";
1015
+ return { tokens:currentTokens, status};
1016
+ }
1063
1017
 
1064
1018
  loginCallbackWithAutoTokensRenewPromise:Promise<loginCallbackResult> = null;
1065
1019
  loginCallbackWithAutoTokensRenewAsync():Promise<loginCallbackResult>{
@@ -1077,24 +1031,39 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
1077
1031
  return userInfoAsync(this);
1078
1032
  }
1079
1033
 
1080
- async destroyAsync() {
1034
+ async destroyAsync(status) {
1081
1035
  timer.clearTimeout(this.timeoutId);
1082
1036
  this.timeoutId=null;
1083
1037
  if(this.checkSessionIFrame){
1084
1038
  this.checkSessionIFrame.stop();
1085
1039
  }
1086
1040
  if(this.serviceWorker){
1087
- await this.serviceWorker.clearAsync();
1041
+ await this.serviceWorker.clearAsync(status);
1088
1042
  }
1089
1043
  if(this.session){
1090
- await this.session.clearAsync();
1044
+ await this.session.clearAsync(status);
1091
1045
  }
1092
1046
  this.tokens = null;
1093
1047
  this.userInfo = null;
1094
- this.events = [];
1095
-
1048
+ // this.events = [];
1096
1049
  }
1097
1050
 
1051
+ async logoutSameTabAsync(clientId, sub){
1052
+ // @ts-ignore
1053
+ if(this.configuration.monitor_session&& this.configuration.client_id === clientId && sub && this.tokens && this.tokens.idTokenPayload && this.tokens.idTokenPayload.sub === sub) {
1054
+ this.publishEvent(eventNames.logout_from_same_tab, {"message": sub});
1055
+ await this.destroyAsync("LOGGED_OUT");
1056
+ }
1057
+ }
1058
+
1059
+ async logoutOtherTabAsync(clientId, sub){
1060
+ // @ts-ignore
1061
+ if(this.configuration.monitor_session && this.configuration.client_id === clientId && sub && this.tokens && this.tokens.idTokenPayload && this.tokens.idTokenPayload.sub === sub) {
1062
+ await this.destroyAsync("LOGGED_OUT");
1063
+ this.publishEvent(eventNames.logout_from_another_tab, {message : "SessionMonitor", "sub": sub});
1064
+ }
1065
+ }
1066
+
1098
1067
  async logoutAsync(callbackPathOrUrl: string | undefined = undefined, extras: StringMap = null) {
1099
1068
  const configuration = this.configuration;
1100
1069
  const oidcServerConfiguration = await this.initAsync(configuration.authority, configuration.authority_configuration);
@@ -1111,7 +1080,16 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
1111
1080
  const url = isUri ? callbackPathOrUrl : window.location.origin + path;
1112
1081
  // @ts-ignore
1113
1082
  const idToken = this.tokens ? this.tokens.idToken : "";
1114
- await this.destroyAsync();
1083
+ // @ts-ignore
1084
+ const sub = this.tokens && this.tokens.idTokenPayload ? this.tokens.idTokenPayload.sub : null;
1085
+ await this.destroyAsync("LOGGED_OUT");
1086
+ for (const [key, oidc] of Object.entries(oidcDatabase)) {
1087
+ if(oidc !== this) {
1088
+ // @ts-ignore
1089
+ await oidc.logoutSameTabAsync(this.configuration.client_id, sub);
1090
+ }
1091
+ }
1092
+
1115
1093
  if(oidcServerConfiguration.endSessionEndpoint) {
1116
1094
  let extraQueryString = "";
1117
1095
  if(extras){