@axa-fr/react-oidc 6.0.0-beta13 → 6.0.0-beta16

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.
@@ -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,29 +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
- const data = {
57
- accessToken: tokens.access_token,
58
- expiresIn: tokens.expires_in,
59
- idToken: tokens.id_token,
60
- refreshToken: tokens.refresh_token,
61
- scope: tokens.scope,
62
- tokenType: tokens.token_type,
63
- issuedAt: tokens.issued_at
64
- };
65
-
66
- if(tokens.accessTokenPayload !== undefined){
67
- // @ts-ignore
68
- data.accessTokenPayload = tokens.accessTokenPayload;
69
- }
70
-
71
- return { success : true,
72
- data
51
+ return {
52
+ success : true,
53
+ data: parseOriginalTokens(tokens)
73
54
  };
74
55
  }
75
56
 
@@ -89,7 +70,6 @@ const internalFetch = async (url, headers, numberRetry=0) => {
89
70
  throw e;
90
71
  }
91
72
  } else {
92
-
93
73
  console.error(e.message);
94
74
  throw e; // rethrow other unexpected errors
95
75
  }
@@ -115,36 +95,6 @@ export class OidcAuthorizationServiceConfiguration extends AuthorizationServiceC
115
95
 
116
96
  }
117
97
 
118
- const idTokenPayload = (token) => {
119
- const base64Url = token.split('.')[1];
120
- const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
121
- const jsonPayload = decodeURIComponent(atob(base64).split('').map(function (c) {
122
- return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
123
- }).join(''));
124
-
125
- return JSON.parse(jsonPayload);
126
- }
127
-
128
- const countLetter = (str, find)=> {
129
- return (str.split(find)).length - 1;
130
- }
131
-
132
- const extractAccessTokenPayload = tokens => {
133
- if(tokens.accessTokenPayload)
134
- {
135
- return tokens.accessTokenPayload;
136
- }
137
- const accessToken = tokens.accessToken;
138
- try{
139
- if (!accessToken || countLetter(accessToken,'.') != 2) {
140
- return null;
141
- }
142
- return JSON.parse(atob(accessToken.split('.')[1]));
143
- } catch (e) {
144
- console.warn(e);
145
- }
146
- return null;
147
- };
148
98
 
149
99
  export interface StringMap {
150
100
  [key: string]: string;
@@ -200,42 +150,45 @@ const loginCallbackWithAutoTokensRenewAsync = async (oidc) => {
200
150
  }
201
151
 
202
152
  const autoRenewTokens = (oidc, refreshToken, expiresAt) => {
203
- const refreshTimeBeforeTokensExpirationInSecond = oidc.configuration.refresh_time_before_tokens_expiration_in_second ?? 60;
153
+ const refreshTimeBeforeTokensExpirationInSecond = oidc.configuration.refresh_time_before_tokens_expiration_in_second;
204
154
  return timer.setTimeout(async () => {
205
- const currentTimeUnixSecond = new Date().getTime() /1000;
206
- const timeInfo = { timeLeft: Math.round(((expiresAt - refreshTimeBeforeTokensExpirationInSecond) - currentTimeUnixSecond))};
155
+ const timeLeft = computeTimeLeft(refreshTimeBeforeTokensExpirationInSecond, expiresAt);
156
+ const timeInfo = { timeLeft };
207
157
  oidc.publishEvent(Oidc.eventNames.token_timer, timeInfo);
208
- if(currentTimeUnixSecond > (expiresAt - refreshTimeBeforeTokensExpirationInSecond)) {
209
- const tokens = await oidc.refreshTokensAsync(refreshToken);
210
- oidc.tokens= await setTokensAsync(oidc.serviceWorker, tokens);
158
+ const {tokens, status} = await oidc.synchroniseTokensAsync(refreshToken);
159
+ oidc.tokens= tokens;
211
160
  if(!oidc.serviceWorker){
212
161
  await oidc.session.setTokens(oidc.tokens);
213
162
  }
214
163
  if(!oidc.tokens){
215
- if(oidc.checkSessionIFrame){
216
- oidc.checkSessionIFrame.stop();
217
- oidc.checkSessionIFrame = null;
218
- }
164
+ await oidc.destroyAsync(status);
219
165
  return;
220
166
  }
221
- oidc.publishEvent(Oidc.eventNames.token_renewed, {});
167
+
222
168
  if(oidc.timeoutId) {
223
169
  oidc.timeoutId = autoRenewTokens(oidc, tokens.refreshToken, oidc.tokens.expiresAt);
224
170
  }
225
- } else{
226
- const tokens = await oidc.syncTokensAsync();
227
- if(tokens && oidc.timeoutId) {
228
- oidc.timeoutId = autoRenewTokens(oidc, tokens.refreshToken, tokens.expiresAt);
229
- }
230
- }
171
+
231
172
  }, 1000);
232
173
  }
233
174
 
234
175
  const getLoginSessionKey = (configurationName:string, redirectUri:string) => {
235
176
  return `oidc_login.${configurationName}:${redirectUri}`;
236
177
  }
178
+
179
+ const setLoginParams = (configurationName:string, redirectUri:string, data) =>{
180
+ const sessionKey = getLoginSessionKey(configurationName, redirectUri);
181
+ getLoginParamsCache = data
182
+ sessionStorage[sessionKey] = JSON.stringify(data);
183
+ }
184
+
185
+ let getLoginParamsCache = null;
237
186
  const getLoginParams = (configurationName, redirectUri) => {
238
- return JSON.parse(sessionStorage[getLoginSessionKey(configurationName, redirectUri)]);
187
+ const dataString = sessionStorage[getLoginSessionKey(configurationName, redirectUri)];
188
+ if(!getLoginParamsCache){
189
+ getLoginParamsCache = JSON.parse(dataString);
190
+ }
191
+ return getLoginParamsCache;
239
192
  }
240
193
 
241
194
  const userInfoAsync = async (oidc) => {
@@ -245,11 +198,15 @@ const userInfoAsync = async (oidc) => {
245
198
  if(!oidc.tokens){
246
199
  return null;
247
200
  }
248
- if(oidc.syncTokensAsyncPromise){
249
- await oidc.syncTokensAsyncPromise;
250
- }
251
201
  const accessToken = oidc.tokens.accessToken;
252
-
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
+
253
210
  const oidcServerConfiguration = await oidc.initAsync(oidc.configuration.authority, oidc.configuration.authority_configuration);
254
211
  const url = oidcServerConfiguration.userInfoEndpoint;
255
212
  const fetchUserInfo = async (accessToken) => {
@@ -271,28 +228,6 @@ const userInfoAsync = async (oidc) => {
271
228
  return userInfo;
272
229
  }
273
230
 
274
- const setTokensAsync = async (serviceWorker, tokens) =>{
275
- let accessTokenPayload;
276
- /*if(tokens == null){
277
- if(serviceWorker){
278
- await serviceWorker.clearAsync();
279
- }
280
- return null;
281
- }*/
282
- if(tokens.accessTokenPayload !== undefined) {
283
- accessTokenPayload = tokens.accessTokenPayload;//await serviceWorker.getAccessTokenPayloadAsync();
284
- }
285
- else {
286
- accessTokenPayload = extractAccessTokenPayload(tokens);
287
- }
288
- const _idTokenPayload = idTokenPayload(tokens.idToken);
289
-
290
- const idTokenExipreAt =(_idTokenPayload && _idTokenPayload.exp) ? _idTokenPayload.exp: Number.MAX_VALUE;
291
- const accessTokenExpiresAt = (accessTokenPayload && accessTokenPayload.exp)? accessTokenPayload.exp : tokens.issuedAt + tokens.expiresIn;
292
- const expiresAt = idTokenExipreAt < accessTokenExpiresAt ? idTokenExipreAt : accessTokenExpiresAt;
293
- return {...tokens, idTokenPayload: _idTokenPayload, accessTokenPayload, expiresAt};
294
- }
295
-
296
231
  const eventNames = {
297
232
  service_worker_not_supported_by_browser: "service_worker_not_supported_by_browser",
298
233
  token_aquired: "token_aquired",
@@ -390,7 +325,12 @@ export class Oidc {
390
325
  silent_login_uri = `${configuration.silent_redirect_uri.replace("-callback", "").replace("callback", "")}-login`;
391
326
  }
392
327
 
393
- 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
+ };
394
334
  this.configurationName= configurationName;
395
335
  this.tokens = null
396
336
  this.userInfo = null;
@@ -398,7 +338,7 @@ export class Oidc {
398
338
  this.timeoutId = null;
399
339
  this.serviceWorker = null;
400
340
  this.session = null;
401
- this.refreshTokensAsync.bind(this);
341
+ this.synchroniseTokensAsync.bind(this);
402
342
  this.loginCallbackWithAutoTokensRenewAsync.bind(this);
403
343
  this.initAsync.bind(this);
404
344
  this.loginCallbackAsync.bind(this);
@@ -468,17 +408,6 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
468
408
  if (!this.configuration.silent_redirect_uri || !this.configuration.silent_login_uri) {
469
409
  return Promise.resolve(null);
470
410
  }
471
- while (document.hidden) {
472
- await sleepAsync(1000);
473
- this.publishEvent(eventNames.silentLoginAsync, {message:"wait because document is hidden"});
474
- }
475
-
476
- let numberTryOnline = 6;
477
- while (!navigator.onLine && numberTryOnline > 0) {
478
- await sleepAsync(1000);
479
- numberTryOnline--;
480
- this.publishEvent(eventNames.refreshTokensAsync, {message: `wait because navigator is offline try ${numberTryOnline}` });
481
- }
482
411
 
483
412
  try {
484
413
  this.publishEvent(eventNames.silentLoginAsync_begin, {});
@@ -549,7 +478,7 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
549
478
  }
550
479
  }
551
480
  };
552
- const silentSigninTimeout = configuration.silent_login_timeout ?? 12000
481
+ const silentSigninTimeout = configuration.silent_login_timeout;
553
482
  setTimeout(() => {
554
483
  if (!isResolved) {
555
484
  self.publishEvent(eventNames.silentLoginAsync_error, {reason: "timeout"});
@@ -583,8 +512,7 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
583
512
 
584
513
  const serviceWorker = await initWorkerAsync(this.configuration.service_worker_relative_url, this.configurationName);
585
514
  const storage = serviceWorker ? window.localStorage : null;
586
- const initAsyncPromise = await fetchFromIssuer(authority, this.configuration.authority_time_cache_wellknowurl_in_second ?? 60 * 60, storage);
587
- return initAsyncPromise;
515
+ return await fetchFromIssuer(authority, this.configuration.authority_time_cache_wellknowurl_in_second ?? 60 * 60, storage);
588
516
  }
589
517
 
590
518
  tryKeepExistingSessionPromise = null;
@@ -608,16 +536,7 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
608
536
  if (tokens) {
609
537
  serviceWorker.startKeepAliveServiceWorker();
610
538
  // @ts-ignore
611
- const reformattedToken = {
612
- accessToken : tokens.access_token,
613
- refreshToken : tokens.refresh_token,
614
- expiresIn: tokens.expires_in,
615
- idToken: tokens.id_token,
616
- scope: tokens.scope,
617
- tokenType: tokens.token_type,
618
- issuedAt: tokens.issued_at
619
- }
620
- this.tokens = await setTokensAsync(serviceWorker, reformattedToken);
539
+ this.tokens = tokens;
621
540
  this.serviceWorker = serviceWorker;
622
541
  // @ts-ignore
623
542
  this.timeoutId = autoRenewTokens(this, this.tokens.refreshToken, this.tokens.expiresAt);
@@ -644,7 +563,7 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
644
563
  const {tokens} = await session.initAsync();
645
564
  if (tokens) {
646
565
  // @ts-ignore
647
- this.tokens = await setTokensAsync(serviceWorker, tokens);
566
+ this.tokens = setTokens(tokens);
648
567
  //session.setTokens(this.tokens);
649
568
  this.session = session;
650
569
  // @ts-ignore
@@ -682,7 +601,7 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
682
601
  }
683
602
 
684
603
  loginPromise: Promise<any>=null;
685
- 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) {
686
605
  if(this.loginPromise !== null){
687
606
  return this.loginPromise;
688
607
  }
@@ -699,9 +618,8 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
699
618
  scope = configuration.scope;
700
619
  }
701
620
 
702
- const sessionKey = getLoginSessionKey(this.configurationName, redirectUri);
703
- sessionStorage[sessionKey] = JSON.stringify({callbackPath: url, extras, state});
704
-
621
+ setLoginParams(this.configurationName, redirectUri, {callbackPath: url, extras, state});
622
+
705
623
  let serviceWorker = await initWorkerAsync(configuration.service_worker_relative_url, this.configurationName);
706
624
  const oidcServerConfiguration = await this.initAsync(configuration.authority, configuration.authority_configuration);
707
625
  let storage;
@@ -776,7 +694,7 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
776
694
  for (const [key, oidc] of Object.entries(oidcDatabase)) {
777
695
  //if(oidc !== this) {
778
696
  // @ts-ignore
779
- await oidc.logoutOtherTabAsync(idTokenPayload.sub);
697
+ await oidc.logoutOtherTabAsync(this.configuration.client_id, idTokenPayload.sub);
780
698
  //}
781
699
  }
782
700
  //await this.destroyAsync();
@@ -808,7 +726,7 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
808
726
  const response = await this._loginCallbackAsync(isSilenSignin);
809
727
  // @ts-ignore
810
728
  const tokens = response.tokens;
811
- const parsedTokens = await setTokensAsync(this.serviceWorker, tokens);
729
+ const parsedTokens = setTokens(tokens);
812
730
  this.tokens = parsedTokens;
813
731
  if(!this.serviceWorker){
814
732
  await this.session.setTokens(parsedTokens);
@@ -943,158 +861,154 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
943
861
  }
944
862
  }
945
863
 
946
- async refreshTokensAsync(refreshToken) {
864
+ async synchroniseTokensAsync(refreshToken, index=0) {
947
865
 
866
+ if (document.hidden) {
867
+ await sleepAsync(1000);
868
+ this.publishEvent(eventNames.refreshTokensAsync, {message: "wait because document is hidden"});
869
+ return await this.synchroniseTokensAsync(refreshToken, index);
870
+ }
871
+ let numberTryOnline = 6;
872
+ while (!navigator.onLine && numberTryOnline > 0) {
873
+ await sleepAsync(1000);
874
+ numberTryOnline--;
875
+ this.publishEvent(eventNames.refreshTokensAsync, {message: `wait because navigator is offline try ${numberTryOnline}` });
876
+ }
877
+
878
+ const configuration = this.configuration;
948
879
  const localsilentLoginAsync= async () => {
949
880
  try {
950
881
  const loginParams = getLoginParams(this.configurationName, configuration.redirect_uri);
951
- const silent_token_response = await this.silentLoginAsync(loginParams.extras, loginParams.state);
882
+ const silent_token_response = await this.silentLoginAsync({
883
+ ...loginParams.extras,
884
+ prompt: "none"
885
+ }, loginParams.state);
952
886
  if (silent_token_response) {
953
- return silent_token_response.tokens;
887
+ this.publishEvent(Oidc.eventNames.token_renewed, {});
888
+ return {tokens:silent_token_response.tokens, status:"LOGGED"};
954
889
  }
955
890
  } catch (exceptionSilent) {
956
891
  console.error(exceptionSilent);
892
+ this.publishEvent(eventNames.refreshTokensAsync_silent_error, {message: "exceptionSilent" ,exception: exceptionSilent.message});
893
+ if(exceptionSilent && exceptionSilent.message && exceptionSilent.message.startsWith("oidc")){
894
+ this.publishEvent(eventNames.refreshTokensAsync_error, {message: `refresh token silent` });
895
+ return {tokens:null, status:"SESSION_LOST"};
896
+ }
897
+ await sleepAsync(1000);
898
+ throw exceptionSilent;
957
899
  }
958
- if(this.timeoutId){
959
- timer.clearTimeout(this.timeoutId);
960
- this.timeoutId=null;
961
- }
962
- this.publishEvent(eventNames.refreshTokensAsync_error, {message: "refresh token and silent refresh failed"});
963
- return null;
900
+ this.publishEvent(eventNames.refreshTokensAsync_error, {message: `refresh token silent return` });
901
+ return {tokens:null, status:"SESSION_LOST"};
964
902
  }
965
-
966
- const configuration = this.configuration;
967
- const clientId = configuration.client_id;
968
- const redirectUri = configuration.redirect_uri;
969
- const authority = configuration.authority;
970
-
971
- if(!refreshToken)
972
- {
973
- return await localsilentLoginAsync();
974
- }
975
-
976
- let extras = {};
977
- if(configuration.token_request_extras) {
978
- for (let [key, value] of Object.entries(configuration.token_request_extras)) {
979
- extras[key] = value;
980
- }
981
- }
982
- const oidcServerConfiguration = await this.initAsync(authority, configuration.authority_configuration);
983
-
984
- const details = {
985
- client_id: clientId,
986
- redirect_uri: redirectUri,
987
- grant_type: GRANT_TYPE_REFRESH_TOKEN,
988
- refresh_token: refreshToken,
989
- };
990
903
 
991
- let index = 0;
992
- while (index <=4) {
904
+ if (index <=4) {
993
905
  try {
994
- this.publishEvent(eventNames.refreshTokensAsync_begin, {refreshToken:refreshToken, tryNumber: index});
995
- if(index > 1) {
996
- while (document.hidden) {
997
- await sleepAsync(1000);
998
- this.publishEvent(eventNames.refreshTokensAsync, {message: "wait because document is hidden"});
999
- }
1000
- let numberTryOnline = 6;
1001
- while (!navigator.onLine && numberTryOnline > 0) {
1002
- await sleepAsync(1000);
1003
- numberTryOnline--;
1004
- this.publishEvent(eventNames.refreshTokensAsync, {message: `wait because navigator is offline try ${numberTryOnline}` });
1005
- }
1006
- }
1007
- const tokenResponse = await performTokenRequestAsync(oidcServerConfiguration.tokenEndpoint, details, extras)
1008
- if (tokenResponse.success) {
1009
- this.publishEvent(eventNames.refreshTokensAsync_end, {success: tokenResponse.success});
1010
- return tokenResponse.data;
1011
- } else {
1012
- this.publishEvent(eventNames.refreshTokensAsync_silent_error, {message: "bad request" , tokenResponse: tokenResponse});
906
+
907
+ if(!refreshToken)
908
+ {
909
+ this.publishEvent(eventNames.refreshTokensAsync_begin, {refreshToken:refreshToken, tryNumber: index});
1013
910
  return await localsilentLoginAsync();
1014
911
  }
912
+ const { status, tokens } = await this.syncTokensInfoAsync(configuration, this.configurationName, this.tokens);
913
+ switch (status) {
914
+ case "SESSION_LOST":
915
+ this.publishEvent(eventNames.refreshTokensAsync_error, {message: `refresh token session lost` });
916
+ return {tokens:null, status:"SESSION_LOST"};
917
+ case "NOT_CONNECTED":
918
+ return {tokens:null, status:null};
919
+ case "TOKENS_VALID":
920
+ case "TOKEN_UPDATED_BY_ANOTHER_TAB_TOKENS_VALID":
921
+ return {tokens, status:"LOGGED_IN"};
922
+ case "LOGOUT_FROM_ANOTHER_TAB":
923
+ this.publishEvent(eventNames.logout_from_another_tab, {"status": "session syncTokensAsync"});
924
+ return {tokens:null, status:"LOGGED_OUT"};
925
+ case "REQUIRE_SYNC_TOKENS":
926
+ this.publishEvent(eventNames.refreshTokensAsync_begin, {refreshToken:refreshToken, status, tryNumber: index});
927
+ return await localsilentLoginAsync();
928
+ default:
929
+ this.publishEvent(eventNames.refreshTokensAsync_begin, {refreshToken:refreshToken, status, tryNumber: index});
930
+ const clientId = configuration.client_id;
931
+ const redirectUri = configuration.redirect_uri;
932
+ const authority = configuration.authority;
933
+ let extras = {};
934
+ if(configuration.token_request_extras) {
935
+ for (let [key, value] of Object.entries(configuration.token_request_extras)) {
936
+ extras[key] = value;
937
+ }
938
+ }
939
+ const details = {
940
+ client_id: clientId,
941
+ redirect_uri: redirectUri,
942
+ grant_type: GRANT_TYPE_REFRESH_TOKEN,
943
+ refresh_token: tokens.refreshToken,
944
+ };
945
+ const oidcServerConfiguration = await this.initAsync(authority, configuration.authority_configuration);
946
+ const tokenResponse = await performTokenRequestAsync(oidcServerConfiguration.tokenEndpoint, details, extras)
947
+ if (tokenResponse.success) {
948
+ this.publishEvent(eventNames.refreshTokensAsync_end, {success: tokenResponse.success});
949
+ this.publishEvent(Oidc.eventNames.token_renewed, {});
950
+ return {tokens: tokenResponse.data, status:"LOGGED_IN"};
951
+ } else {
952
+ this.publishEvent(eventNames.refreshTokensAsync_silent_error, {
953
+ message: "bad request",
954
+ tokenResponse: tokenResponse
955
+ });
956
+ return await this.synchroniseTokensAsync(null, index+1);
957
+ }
958
+ }
1015
959
  } catch (exception) {
1016
960
  console.error(exception);
1017
961
  this.publishEvent(eventNames.refreshTokensAsync_silent_error, {message: "exception" ,exception: exception.message});
962
+ return this.synchroniseTokensAsync(refreshToken, index+1);
1018
963
  }
1019
- index++;
1020
964
  }
1021
-
965
+
966
+ this.publishEvent(eventNames.refreshTokensAsync_error, {message: `refresh token` });
967
+ return {tokens:null, status:"SESSION_LOST"};
1022
968
  }
1023
969
 
1024
- syncTokensAsyncPromise=null;
1025
- async syncTokensAsync() {
1026
-
1027
- const localSyncTokensAsync = async () => {
1028
- // 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)
1029
- const configuration = this.configuration;
1030
- if (!this.tokens) {
1031
- return null;
1032
- }
970
+ async syncTokensInfoAsync(configuration, configurationName, currentTokens) {
971
+ // 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)
972
+ //const configuration = this.configuration;
973
+ if (!currentTokens) {
974
+ return { tokens : null, status: "NOT_CONNECTED"};
975
+ }
1033
976
 
1034
- const oidcServerConfiguration = await this.initAsync(configuration.authority, configuration.authority_configuration);
1035
- const serviceWorker = await initWorkerAsync(configuration.service_worker_relative_url, this.configurationName);
1036
- if (serviceWorker) {
1037
- const {isLogin} = await serviceWorker.initAsync(oidcServerConfiguration, "syncTokensAsync");
1038
- if (isLogin == false) {
1039
- this.publishEvent(eventNames.logout_from_another_tab, {"message": "service worker syncTokensAsync"});
1040
- await this.destroyAsync();
1041
- return null;
1042
- } else if (isLogin == null) {
1043
- try {
1044
- this.publishEvent(eventNames.syncTokensAsync_begin, {});
1045
- const loginParams = getLoginParams(this.configurationName, configuration.redirect_uri);
1046
- const silent_token_response = await this.silentLoginAsync({...loginParams.extras,prompt: "none"}, loginParams.state);
1047
- if (silent_token_response && silent_token_response.tokens) {
1048
- this.tokens = await setTokensAsync(serviceWorker, silent_token_response.tokens);
1049
- this.publishEvent(eventNames.syncTokensAsync_end, {});
1050
- return this.tokens;
1051
- } else {
1052
- this.publishEvent(eventNames.syncTokensAsync_error, {message: "no token found in result"});
1053
- if (this.timeoutId) {
1054
- timer.clearTimeout(this.timeoutId);
1055
- this.timeoutId = null;
1056
- }
1057
- this.publishEvent(eventNames.syncTokensAsync_end, {});
1058
- return null;
1059
- }
1060
- } catch (exceptionSilent) {
1061
- console.error(exceptionSilent);
1062
- this.publishEvent(eventNames.syncTokensAsync_error, exceptionSilent);
1063
- if (this.timeoutId) {
1064
- timer.clearTimeout(this.timeoutId);
1065
- this.timeoutId = null;
1066
- }
1067
- this.publishEvent(eventNames.syncTokensAsync_end, {});
1068
- return null;
1069
- }
1070
- }
1071
- } else {
1072
- const session = initSession(this.configurationName, configuration.redirect_uri, configuration.storage ?? sessionStorage);
1073
- const {tokens} = await session.initAsync();
1074
- if (!tokens) {
1075
- this.publishEvent(eventNames.logout_from_another_tab, {"message": "session syncTokensAsync"});
1076
- await this.destroyAsync();
1077
- return null;
977
+ const oidcServerConfiguration = await this.initAsync(configuration.authority, configuration.authority_configuration);
978
+ const serviceWorker = await initWorkerAsync(configuration.service_worker_relative_url, configurationName);
979
+ if (serviceWorker) {
980
+ const {status, tokens} = await serviceWorker.initAsync(oidcServerConfiguration, "syncTokensAsync");
981
+ if (status == "LOGGED_OUT") {
982
+ return {tokens: null, status: "LOGOUT_FROM_ANOTHER_TAB"};
983
+ }else if (status == "SESSIONS_LOST") {
984
+ return { tokens : null, status: "SESSIONS_LOST"};
985
+ } else if (!status || !tokens) {
986
+ return { tokens : null, status: "REQUIRE_SYNC_TOKENS"};
987
+ } else if(tokens.issuedAt !== currentTokens.issuedAt) {
988
+ const timeLeft = computeTimeLeft(configuration.refresh_time_before_tokens_expiration_in_second, tokens.expiresAt);
989
+ const status = (timeLeft > 0) ? "TOKEN_UPDATED_BY_ANOTHER_TAB_TOKENS_VALID" : "TOKEN_UPDATED_BY_ANOTHER_TAB_TOKENS_INVALID";
990
+ return { tokens : tokens, status };
991
+ }
992
+ } else {
993
+ const session = initSession(configurationName, configuration.redirect_uri, configuration.storage ?? sessionStorage);
994
+ const {tokens, status } = await session.initAsync();
995
+ if (!tokens) {
996
+ return {tokens: null, status: "LOGOUT_FROM_ANOTHER_TAB"};
997
+ } else if (status == "SESSIONS_LOST") {
998
+ return { tokens : null, status: "SESSIONS_LOST"};
1078
999
  }
1000
+ else if(tokens.issuedAt !== currentTokens.issuedAt){
1001
+ const timeLeft = computeTimeLeft(configuration.refresh_time_before_tokens_expiration_in_second, tokens.expiresAt);
1002
+ const status = (timeLeft > 0) ? "TOKEN_UPDATED_BY_ANOTHER_TAB_TOKENS_VALID" : "TOKEN_UPDATED_BY_ANOTHER_TAB_TOKENS_INVALID";
1003
+ return { tokens : tokens, status };
1079
1004
  }
1080
- return this.tokens;
1081
- }
1082
-
1083
- if(this.syncTokensAsyncPromise){
1084
- return this.syncTokensAsyncPromise;
1085
1005
  }
1086
1006
 
1087
- this.syncTokensAsyncPromise = localSyncTokensAsync().then(result =>{
1088
- if(this.syncTokensAsyncPromise){
1089
- this.syncTokensAsyncPromise = null;
1090
- }
1091
- return result;
1092
- });
1093
-
1094
- return this.syncTokensAsyncPromise
1007
+ const timeLeft = computeTimeLeft(configuration.refresh_time_before_tokens_expiration_in_second, currentTokens.expiresAt);
1008
+ const status = (timeLeft > 0) ? "TOKENS_VALID" : "TOKENS_INVALID";
1009
+ return { tokens:currentTokens, status};
1095
1010
  }
1096
1011
 
1097
-
1098
1012
  loginCallbackWithAutoTokensRenewPromise:Promise<loginCallbackResult> = null;
1099
1013
  loginCallbackWithAutoTokensRenewAsync():Promise<loginCallbackResult>{
1100
1014
  if(this.loginCallbackWithAutoTokensRenewPromise !== null){
@@ -1111,37 +1025,35 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
1111
1025
  return userInfoAsync(this);
1112
1026
  }
1113
1027
 
1114
- async destroyAsync() {
1028
+ async destroyAsync(status) {
1115
1029
  timer.clearTimeout(this.timeoutId);
1116
1030
  this.timeoutId=null;
1117
1031
  if(this.checkSessionIFrame){
1118
1032
  this.checkSessionIFrame.stop();
1119
1033
  }
1120
1034
  if(this.serviceWorker){
1121
- await this.serviceWorker.clearAsync();
1035
+ await this.serviceWorker.clearAsync(status);
1122
1036
  }
1123
1037
  if(this.session){
1124
- await this.session.clearAsync();
1038
+ await this.session.clearAsync(status);
1125
1039
  }
1126
1040
  this.tokens = null;
1127
1041
  this.userInfo = null;
1128
1042
  // this.events = [];
1129
1043
  }
1130
1044
 
1131
- async logoutSameTabAsync(sub){
1045
+ async logoutSameTabAsync(clientId, sub){
1132
1046
  // @ts-ignore
1133
- if(this.configuration.monitor_session && sub && this.tokens && this.tokens.idTokenPayload && this.tokens.idTokenPayload.sub === sub) {
1047
+ if(this.configuration.monitor_session&& this.configuration.client_id === clientId && sub && this.tokens && this.tokens.idTokenPayload && this.tokens.idTokenPayload.sub === sub) {
1134
1048
  this.publishEvent(eventNames.logout_from_same_tab, {"message": sub});
1135
- await this.destroyAsync();
1049
+ await this.destroyAsync("LOGGED_OUT");
1136
1050
  }
1137
1051
  }
1138
1052
 
1139
- async logoutOtherTabAsync(sub){
1053
+ async logoutOtherTabAsync(clientId, sub){
1140
1054
  // @ts-ignore
1141
-
1142
- if(this.configuration.monitor_session && sub && this.tokens && this.tokens.idTokenPayload && this.tokens.idTokenPayload.sub === sub) {
1143
- await this.destroyAsync();
1144
- console.log("logoutOtherTabAsync(sub)" +this.configurationName);
1055
+ if(this.configuration.monitor_session && this.configuration.client_id === clientId && sub && this.tokens && this.tokens.idTokenPayload && this.tokens.idTokenPayload.sub === sub) {
1056
+ await this.destroyAsync("LOGGED_OUT");
1145
1057
  this.publishEvent(eventNames.logout_from_another_tab, {message : "SessionMonitor", "sub": sub});
1146
1058
  }
1147
1059
  }
@@ -1164,11 +1076,11 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
1164
1076
  const idToken = this.tokens ? this.tokens.idToken : "";
1165
1077
  // @ts-ignore
1166
1078
  const sub = this.tokens && this.tokens.idTokenPayload ? this.tokens.idTokenPayload.sub : null;
1167
- await this.destroyAsync();
1079
+ await this.destroyAsync("LOGGED_OUT");
1168
1080
  for (const [key, oidc] of Object.entries(oidcDatabase)) {
1169
1081
  if(oidc !== this) {
1170
1082
  // @ts-ignore
1171
- await oidc.logoutSameTabAsync(sub);
1083
+ await oidc.logoutSameTabAsync(this.configuration.client_id, sub);
1172
1084
  }
1173
1085
  }
1174
1086