@axa-fr/react-oidc 6.0.0-beta12 → 6.0.0-beta15

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,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
-
51
+ console.log(tokens);
56
52
  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
- }
53
+ data: parseOriginalTokens(tokens)
66
54
  };
67
55
  }
68
56
 
@@ -108,36 +96,6 @@ export class OidcAuthorizationServiceConfiguration extends AuthorizationServiceC
108
96
 
109
97
  }
110
98
 
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
99
 
142
100
  export interface StringMap {
143
101
  [key: string]: string;
@@ -193,34 +151,41 @@ const loginCallbackWithAutoTokensRenewAsync = async (oidc) => {
193
151
  }
194
152
 
195
153
  const autoRenewTokens = (oidc, refreshToken, expiresAt) => {
196
- const refreshTimeBeforeTokensExpirationInSecond = oidc.configuration.refresh_time_before_tokens_expiration_in_second ?? 60;
154
+ const refreshTimeBeforeTokensExpirationInSecond = oidc.configuration.refresh_time_before_tokens_expiration_in_second;
197
155
  return timer.setTimeout(async () => {
198
- const currentTimeUnixSecond = new Date().getTime() /1000;
199
- const timeInfo = { timeLeft: Math.round(((expiresAt - refreshTimeBeforeTokensExpirationInSecond) - currentTimeUnixSecond))};
156
+ const timeLeft = computeTimeLeft(refreshTimeBeforeTokensExpirationInSecond, expiresAt);
157
+ const timeInfo = { timeLeft };
200
158
  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);
159
+ // if(timeLeft <= 0) {
160
+ const tokens = await oidc.synchroniseTokensAsync(refreshToken);
161
+ oidc.tokens= tokens;
204
162
  if(!oidc.serviceWorker){
205
163
  await oidc.session.setTokens(oidc.tokens);
206
164
  }
207
165
  if(!oidc.tokens){
208
- if(oidc.checkSessionIFrame){
209
- oidc.checkSessionIFrame.stop();
210
- oidc.checkSessionIFrame = null;
211
- }
166
+ await oidc.destroyAsync();
167
+ oidc.publishEvent(eventNames.refreshTokensAsync_error, {message: `refresh token` });
212
168
  return;
213
169
  }
214
- oidc.publishEvent(Oidc.eventNames.token_renewed, {});
170
+
215
171
  if(oidc.timeoutId) {
216
172
  oidc.timeoutId = autoRenewTokens(oidc, tokens.refreshToken, oidc.tokens.expiresAt);
217
173
  }
218
- } else{
174
+ /*} else{
219
175
  const tokens = await oidc.syncTokensAsync();
176
+ oidc.tokens = tokens;
177
+ if(!oidc.serviceWorker){
178
+ await oidc.session.setTokens(oidc.tokens);
179
+ }
180
+ if(!oidc.tokens){
181
+ oidc.publishEvent(eventNames.refreshTokensAsync_error, {message: `sync` });
182
+ await oidc.destroyAsync();
183
+ return;
184
+ }
220
185
  if(tokens && oidc.timeoutId) {
221
186
  oidc.timeoutId = autoRenewTokens(oidc, tokens.refreshToken, tokens.expiresAt);
222
187
  }
223
- }
188
+ }*/
224
189
  }, 1000);
225
190
  }
226
191
 
@@ -238,11 +203,15 @@ const userInfoAsync = async (oidc) => {
238
203
  if(!oidc.tokens){
239
204
  return null;
240
205
  }
241
- if(oidc.syncTokensAsyncPromise){
242
- await oidc.syncTokensAsyncPromise;
243
- }
244
206
  const accessToken = oidc.tokens.accessToken;
245
-
207
+ if(!accessToken){
208
+ return null;
209
+ }
210
+ // We wait the synchronisation before making a request
211
+ while (oidc.tokens && !isTokensValid(oidc.tokens)){
212
+ await sleepAsync(200);
213
+ }
214
+
246
215
  const oidcServerConfiguration = await oidc.initAsync(oidc.configuration.authority, oidc.configuration.authority_configuration);
247
216
  const url = oidcServerConfiguration.userInfoEndpoint;
248
217
  const fetchUserInfo = async (accessToken) => {
@@ -264,28 +233,6 @@ const userInfoAsync = async (oidc) => {
264
233
  return userInfo;
265
234
  }
266
235
 
267
- const setTokensAsync = async (serviceWorker, tokens) =>{
268
- let accessTokenPayload;
269
- if(tokens == null){
270
- if(serviceWorker){
271
- await serviceWorker.clearAsync();
272
- }
273
- return null;
274
- }
275
- if(serviceWorker){
276
- accessTokenPayload = await serviceWorker.getAccessTokenPayloadAsync();
277
- }
278
- else {
279
- accessTokenPayload = extractAccessTokenPayload(tokens);
280
- }
281
- const _idTokenPayload = idTokenPayload(tokens.idToken);
282
-
283
- const idTokenExipreAt =(_idTokenPayload && _idTokenPayload.exp) ? _idTokenPayload.exp: Number.MAX_VALUE;
284
- const accessTokenExpiresAt = (accessTokenPayload && accessTokenPayload.exp)? accessTokenPayload.exp : tokens.issuedAt + tokens.expiresIn;
285
- const expiresAt = idTokenExipreAt < accessTokenExpiresAt ? idTokenExipreAt : accessTokenExpiresAt;
286
- return {...tokens, idTokenPayload: _idTokenPayload, accessTokenPayload, expiresAt};
287
- }
288
-
289
236
  const eventNames = {
290
237
  service_worker_not_supported_by_browser: "service_worker_not_supported_by_browser",
291
238
  token_aquired: "token_aquired",
@@ -383,7 +330,12 @@ export class Oidc {
383
330
  silent_login_uri = `${configuration.silent_redirect_uri.replace("-callback", "").replace("callback", "")}-login`;
384
331
  }
385
332
 
386
- this.configuration = {...configuration, silent_login_uri};
333
+ this.configuration = {...configuration,
334
+ silent_login_uri,
335
+ monitor_session: configuration.monitor_session ?? true,
336
+ refresh_time_before_tokens_expiration_in_second : configuration.refresh_time_before_tokens_expiration_in_second ?? 60,
337
+ silent_login_timeout: configuration.silent_login_timeout ?? 12000,
338
+ };
387
339
  this.configurationName= configurationName;
388
340
  this.tokens = null
389
341
  this.userInfo = null;
@@ -391,7 +343,7 @@ export class Oidc {
391
343
  this.timeoutId = null;
392
344
  this.serviceWorker = null;
393
345
  this.session = null;
394
- this.refreshTokensAsync.bind(this);
346
+ this.synchroniseTokensAsync.bind(this);
395
347
  this.loginCallbackWithAutoTokensRenewAsync.bind(this);
396
348
  this.initAsync.bind(this);
397
349
  this.loginCallbackAsync.bind(this);
@@ -461,17 +413,6 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
461
413
  if (!this.configuration.silent_redirect_uri || !this.configuration.silent_login_uri) {
462
414
  return Promise.resolve(null);
463
415
  }
464
- while (document.hidden) {
465
- await sleepAsync(1000);
466
- this.publishEvent(eventNames.silentLoginAsync, {message:"wait because document is hidden"});
467
- }
468
-
469
- let numberTryOnline = 6;
470
- while (!navigator.onLine && numberTryOnline > 0) {
471
- await sleepAsync(1000);
472
- numberTryOnline--;
473
- this.publishEvent(eventNames.refreshTokensAsync, {message: `wait because navigator is offline try ${numberTryOnline}` });
474
- }
475
416
 
476
417
  try {
477
418
  this.publishEvent(eventNames.silentLoginAsync_begin, {});
@@ -542,7 +483,7 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
542
483
  }
543
484
  }
544
485
  };
545
- const silentSigninTimeout = configuration.silent_login_timeout ?? 12000
486
+ const silentSigninTimeout = configuration.silent_login_timeout;
546
487
  setTimeout(() => {
547
488
  if (!isResolved) {
548
489
  self.publishEvent(eventNames.silentLoginAsync_error, {reason: "timeout"});
@@ -576,8 +517,7 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
576
517
 
577
518
  const serviceWorker = await initWorkerAsync(this.configuration.service_worker_relative_url, this.configurationName);
578
519
  const storage = serviceWorker ? window.localStorage : null;
579
- const initAsyncPromise = await fetchFromIssuer(authority, this.configuration.authority_time_cache_wellknowurl_in_second ?? 60 * 60, storage);
580
- return initAsyncPromise;
520
+ return await fetchFromIssuer(authority, this.configuration.authority_time_cache_wellknowurl_in_second ?? 60 * 60, storage);
581
521
  }
582
522
 
583
523
  tryKeepExistingSessionPromise = null;
@@ -601,16 +541,7 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
601
541
  if (tokens) {
602
542
  serviceWorker.startKeepAliveServiceWorker();
603
543
  // @ts-ignore
604
- const reformattedToken = {
605
- accessToken : tokens.access_token,
606
- refreshToken : tokens.refresh_token,
607
- expiresIn: tokens.expires_in,
608
- idToken: tokens.id_token,
609
- scope: tokens.scope,
610
- tokenType: tokens.token_type,
611
- issuedAt: tokens.issued_at
612
- }
613
- this.tokens = await setTokensAsync(serviceWorker, reformattedToken);
544
+ this.tokens = tokens;
614
545
  this.serviceWorker = serviceWorker;
615
546
  // @ts-ignore
616
547
  this.timeoutId = autoRenewTokens(this, this.tokens.refreshToken, this.tokens.expiresAt);
@@ -637,7 +568,7 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
637
568
  const {tokens} = await session.initAsync();
638
569
  if (tokens) {
639
570
  // @ts-ignore
640
- this.tokens = await setTokensAsync(serviceWorker, tokens);
571
+ this.tokens = setTokens(tokens);
641
572
  //session.setTokens(this.tokens);
642
573
  this.session = session;
643
574
  // @ts-ignore
@@ -675,7 +606,7 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
675
606
  }
676
607
 
677
608
  loginPromise: Promise<any>=null;
678
- async loginAsync(callbackPath:string=undefined, extras:StringMap=null, installServiceWorker=true, state:string=undefined, isSilentSignin:boolean=false, scope:string=undefined) {
609
+ async loginAsync(callbackPath:string=undefined, extras:StringMap=null, state:string=undefined, isSilentSignin:boolean=false, scope:string=undefined) {
679
610
  if(this.loginPromise !== null){
680
611
  return this.loginPromise;
681
612
  }
@@ -801,7 +732,7 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
801
732
  const response = await this._loginCallbackAsync(isSilenSignin);
802
733
  // @ts-ignore
803
734
  const tokens = response.tokens;
804
- const parsedTokens = await setTokensAsync(this.serviceWorker, tokens);
735
+ const parsedTokens = setTokens(tokens);
805
736
  this.tokens = parsedTokens;
806
737
  if(!this.serviceWorker){
807
738
  await this.session.setTokens(parsedTokens);
@@ -936,158 +867,140 @@ 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
887
  const loginParams = getLoginParams(this.configurationName, configuration.redirect_uri);
944
- const silent_token_response = await this.silentLoginAsync(loginParams.extras, loginParams.state);
888
+ const silent_token_response = await this.silentLoginAsync({
889
+ ...loginParams.extras,
890
+ prompt: "none"
891
+ }, loginParams.state);
945
892
  if (silent_token_response) {
893
+ this.publishEvent(Oidc.eventNames.token_renewed, {});
946
894
  return silent_token_response.tokens;
947
895
  }
948
896
  } catch (exceptionSilent) {
949
- console.error(exceptionSilent);
950
- }
951
- if(this.timeoutId){
952
- timer.clearTimeout(this.timeoutId);
953
- this.timeoutId=null;
897
+ if(exceptionSilent && exceptionSilent.message && exceptionSilent.message.startsWith("oidc")){
898
+ return null;
899
+ }
900
+ throw exceptionSilent;
954
901
  }
955
- this.publishEvent(eventNames.refreshTokensAsync_error, {message: "refresh token and silent refresh failed"});
956
902
  return null;
957
903
  }
958
-
959
- const configuration = this.configuration;
960
- const clientId = configuration.client_id;
961
- const redirectUri = configuration.redirect_uri;
962
- const authority = configuration.authority;
963
-
964
- if(!refreshToken)
965
- {
966
- return await localsilentLoginAsync();
967
- }
968
904
 
969
- let extras = {};
970
- if(configuration.token_request_extras) {
971
- for (let [key, value] of Object.entries(configuration.token_request_extras)) {
972
- extras[key] = value;
973
- }
974
- }
975
- const oidcServerConfiguration = await this.initAsync(authority, configuration.authority_configuration);
976
-
977
- const details = {
978
- client_id: clientId,
979
- redirect_uri: redirectUri,
980
- grant_type: GRANT_TYPE_REFRESH_TOKEN,
981
- refresh_token: refreshToken,
982
- };
983
-
984
- let index = 0;
985
- while (index <=4) {
905
+ if (index <=4) {
986
906
  try {
987
- this.publishEvent(eventNames.refreshTokensAsync_begin, {refreshToken:refreshToken, tryNumber: index});
988
- if(index > 1) {
989
- while (document.hidden) {
990
- await sleepAsync(1000);
991
- this.publishEvent(eventNames.refreshTokensAsync, {message: "wait because document is hidden"});
992
- }
993
- let numberTryOnline = 6;
994
- while (!navigator.onLine && numberTryOnline > 0) {
995
- await sleepAsync(1000);
996
- numberTryOnline--;
997
- this.publishEvent(eventNames.refreshTokensAsync, {message: `wait because navigator is offline try ${numberTryOnline}` });
998
- }
999
- }
1000
- const tokenResponse = await performTokenRequestAsync(oidcServerConfiguration.tokenEndpoint, details, extras)
1001
- if (tokenResponse.success) {
1002
- this.publishEvent(eventNames.refreshTokensAsync_end, {success: tokenResponse.success});
1003
- return tokenResponse.data;
1004
- } else {
1005
- this.publishEvent(eventNames.refreshTokensAsync_silent_error, {message: "bad request" , tokenResponse: tokenResponse});
907
+
908
+ if(!refreshToken)
909
+ {
910
+ this.publishEvent(eventNames.refreshTokensAsync_begin, {refreshToken:refreshToken, tryNumber: index});
1006
911
  return await localsilentLoginAsync();
1007
912
  }
913
+ const { status, tokens } = await this.syncTokensInfoAsync(configuration, this.configurationName, this.tokens);
914
+ // this.publishEvent(eventNames.refreshTokensAsync, {message: `status ${status}` });
915
+ switch (status) {
916
+ case "NOT_CONNECTED":
917
+ return null;
918
+ case "TOKENS_VALID":
919
+ case "TOKEN_UPDATED_BY_ANOTHER_TAB_TOKENS_VALID":
920
+ return tokens;
921
+ case "LOGOUT_FROM_ANOTHER_TAB":
922
+ this.publishEvent(eventNames.logout_from_another_tab, {"status": "session syncTokensAsync"});
923
+ return null;
924
+ case "REQUIRE_SYNC_TOKENS":
925
+ this.publishEvent(eventNames.refreshTokensAsync_begin, {refreshToken:refreshToken, status, tryNumber: index});
926
+ return await localsilentLoginAsync();
927
+ default:
928
+ this.publishEvent(eventNames.refreshTokensAsync_begin, {refreshToken:refreshToken, status, tryNumber: index});
929
+ const clientId = configuration.client_id;
930
+ const redirectUri = configuration.redirect_uri;
931
+ const authority = configuration.authority;
932
+ let extras = {};
933
+ if(configuration.token_request_extras) {
934
+ for (let [key, value] of Object.entries(configuration.token_request_extras)) {
935
+ extras[key] = value;
936
+ }
937
+ }
938
+ const details = {
939
+ client_id: clientId,
940
+ redirect_uri: redirectUri,
941
+ grant_type: GRANT_TYPE_REFRESH_TOKEN,
942
+ refresh_token: tokens.refreshToken,
943
+ };
944
+ const oidcServerConfiguration = await this.initAsync(authority, configuration.authority_configuration);
945
+ const tokenResponse = await performTokenRequestAsync(oidcServerConfiguration.tokenEndpoint, details, extras)
946
+ if (tokenResponse.success) {
947
+ this.publishEvent(eventNames.refreshTokensAsync_end, {success: tokenResponse.success});
948
+ this.publishEvent(Oidc.eventNames.token_renewed, {});
949
+ return tokenResponse.data;
950
+ } else {
951
+ this.publishEvent(eventNames.refreshTokensAsync_silent_error, {
952
+ message: "bad request",
953
+ tokenResponse: tokenResponse
954
+ });
955
+ return await this.synchroniseTokensAsync(null, index+1);
956
+ }
957
+ }
1008
958
  } catch (exception) {
1009
959
  console.error(exception);
1010
960
  this.publishEvent(eventNames.refreshTokensAsync_silent_error, {message: "exception" ,exception: exception.message});
961
+ return this.synchroniseTokensAsync(refreshToken, index+1);
1011
962
  }
1012
- index++;
1013
963
  }
1014
-
964
+ return null;
1015
965
  }
1016
966
 
1017
- syncTokensAsyncPromise=null;
1018
- async syncTokensAsync() {
1019
-
1020
- const localSyncTokensAsync = async () => {
1021
- // 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)
1022
- const configuration = this.configuration;
1023
- if (!this.tokens) {
1024
- return null;
1025
- }
967
+ async syncTokensInfoAsync(configuration, configurationName, currentTokens) {
968
+ // 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)
969
+ //const configuration = this.configuration;
970
+ if (!currentTokens) {
971
+ return { tokens : null, status: "NOT_CONNECTED"};
972
+ }
1026
973
 
1027
- const oidcServerConfiguration = await this.initAsync(configuration.authority, configuration.authority_configuration);
1028
- const serviceWorker = await initWorkerAsync(configuration.service_worker_relative_url, this.configurationName);
1029
- if (serviceWorker) {
1030
- const {isLogin} = await serviceWorker.initAsync(oidcServerConfiguration, "syncTokensAsync");
1031
- if (isLogin == false) {
1032
- this.publishEvent(eventNames.logout_from_another_tab, {"message": "service worker syncTokensAsync"});
1033
- await this.destroyAsync();
1034
- return null;
1035
- } else if (isLogin == null) {
1036
- try {
1037
- this.publishEvent(eventNames.syncTokensAsync_begin, {});
1038
- const loginParams = getLoginParams(this.configurationName, configuration.redirect_uri);
1039
- const silent_token_response = await this.silentLoginAsync({...loginParams.extras,prompt: "none"}, loginParams.state);
1040
- if (silent_token_response && silent_token_response.tokens) {
1041
- this.tokens = await setTokensAsync(serviceWorker, silent_token_response.tokens);
1042
- this.publishEvent(eventNames.syncTokensAsync_end, {});
1043
- return this.tokens;
1044
- } else {
1045
- this.publishEvent(eventNames.syncTokensAsync_error, {message: "no token found in result"});
1046
- if (this.timeoutId) {
1047
- timer.clearTimeout(this.timeoutId);
1048
- this.timeoutId = null;
1049
- }
1050
- this.publishEvent(eventNames.syncTokensAsync_end, {});
1051
- return null;
1052
- }
1053
- } catch (exceptionSilent) {
1054
- console.error(exceptionSilent);
1055
- this.publishEvent(eventNames.syncTokensAsync_error, exceptionSilent);
1056
- if (this.timeoutId) {
1057
- timer.clearTimeout(this.timeoutId);
1058
- this.timeoutId = null;
1059
- }
1060
- this.publishEvent(eventNames.syncTokensAsync_end, {});
1061
- return null;
1062
- }
1063
- }
1064
- } else {
1065
- const session = initSession(this.configurationName, configuration.redirect_uri, configuration.storage ?? sessionStorage);
1066
- const {tokens} = await session.initAsync();
1067
- if (!tokens) {
1068
- this.publishEvent(eventNames.logout_from_another_tab, {"message": "session syncTokensAsync"});
1069
- await this.destroyAsync();
1070
- return null;
1071
- }
974
+ const oidcServerConfiguration = await this.initAsync(configuration.authority, configuration.authority_configuration);
975
+ const serviceWorker = await initWorkerAsync(configuration.service_worker_relative_url, configurationName);
976
+ if (serviceWorker) {
977
+ const {isLogin, tokens} = await serviceWorker.initAsync(oidcServerConfiguration, "syncTokensAsync");
978
+ if (isLogin == false) {
979
+ return { tokens : null, status: "LOGOUT_FROM_ANOTHER_TAB"};
980
+ } else if (isLogin == null) {
981
+ return { tokens : null, status: "REQUIRE_SYNC_TOKENS"};
982
+ } else if(tokens.issuedAt !== currentTokens.issuedAt) {
983
+ const timeLeft = computeTimeLeft(configuration.refresh_time_before_tokens_expiration_in_second, tokens.expiresAt);
984
+ const status = (timeLeft > 0) ? "TOKEN_UPDATED_BY_ANOTHER_TAB_TOKENS_VALID" : "TOKEN_UPDATED_BY_ANOTHER_TAB_TOKENS_INVALID";
985
+ return { tokens : tokens, status };
986
+ }
987
+ } else {
988
+ const session = initSession(configurationName, configuration.redirect_uri, configuration.storage ?? sessionStorage);
989
+ const {tokens} = await session.initAsync();
990
+ if (!tokens) {
991
+ return { tokens : null, status: "LOGOUT_FROM_ANOTHER_TAB"};
992
+ } else if(tokens.issuedAt !== currentTokens.issuedAt){
993
+ const timeLeft = computeTimeLeft(configuration.refresh_time_before_tokens_expiration_in_second, tokens.expiresAt);
994
+ const status = (timeLeft > 0) ? "TOKEN_UPDATED_BY_ANOTHER_TAB_TOKENS_VALID" : "TOKEN_UPDATED_BY_ANOTHER_TAB_TOKENS_INVALID";
995
+ return { tokens : tokens, status };
1072
996
  }
1073
- return this.tokens;
1074
- }
1075
-
1076
- if(this.syncTokensAsyncPromise){
1077
- return this.syncTokensAsyncPromise;
1078
997
  }
1079
998
 
1080
- this.syncTokensAsyncPromise = localSyncTokensAsync().then(result =>{
1081
- if(this.syncTokensAsyncPromise){
1082
- this.syncTokensAsyncPromise = null;
1083
- }
1084
- return result;
1085
- });
1086
-
1087
- return this.syncTokensAsyncPromise
999
+ const timeLeft = computeTimeLeft(configuration.refresh_time_before_tokens_expiration_in_second, currentTokens.expiresAt);
1000
+ const status = (timeLeft > 0) ? "TOKENS_VALID" : "TOKENS_INVALID";
1001
+ return { tokens:currentTokens, status};
1088
1002
  }
1089
1003
 
1090
-
1091
1004
  loginCallbackWithAutoTokensRenewPromise:Promise<loginCallbackResult> = null;
1092
1005
  loginCallbackWithAutoTokensRenewAsync():Promise<loginCallbackResult>{
1093
1006
  if(this.loginCallbackWithAutoTokensRenewPromise !== null){
@@ -1131,7 +1044,6 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
1131
1044
 
1132
1045
  async logoutOtherTabAsync(sub){
1133
1046
  // @ts-ignore
1134
-
1135
1047
  if(this.configuration.monitor_session && sub && this.tokens && this.tokens.idTokenPayload && this.tokens.idTokenPayload.sub === sub) {
1136
1048
  await this.destroyAsync();
1137
1049
  console.log("logoutOtherTabAsync(sub)" +this.configurationName);
@@ -0,0 +1,104 @@
1
+ const idTokenPayload = (token) => {
2
+ const base64Url = token.split('.')[1];
3
+ const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
4
+ const jsonPayload = decodeURIComponent(atob(base64).split('').map(function (c) {
5
+ return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
6
+ }).join(''));
7
+
8
+ return JSON.parse(jsonPayload);
9
+ }
10
+
11
+ const countLetter = (str, find)=> {
12
+ return (str.split(find)).length - 1;
13
+ }
14
+
15
+ const extractAccessTokenPayload = tokens => {
16
+ if(tokens.accessTokenPayload)
17
+ {
18
+ return tokens.accessTokenPayload;
19
+ }
20
+ const accessToken = tokens.accessToken;
21
+ try{
22
+ if (!accessToken || countLetter(accessToken,'.') !== 2) {
23
+ return null;
24
+ }
25
+ return JSON.parse(atob(accessToken.split('.')[1]));
26
+ } catch (e) {
27
+ console.warn(e);
28
+ }
29
+ return null;
30
+ };
31
+
32
+
33
+ export const setTokens = (tokens) =>{
34
+
35
+ if(!tokens){
36
+ return null;
37
+ }
38
+ let accessTokenPayload;
39
+
40
+ if(!tokens.issuedAt) {
41
+ const currentTimeUnixSecond = new Date().getTime() /1000;
42
+ tokens.issuedAt = currentTimeUnixSecond;
43
+ }
44
+
45
+ if(tokens.accessTokenPayload !== undefined) {
46
+ accessTokenPayload = tokens.accessTokenPayload;
47
+ }
48
+ else {
49
+ accessTokenPayload = extractAccessTokenPayload(tokens);
50
+ }
51
+ const _idTokenPayload = idTokenPayload(tokens.idToken);
52
+
53
+ const idTokenExipreAt =(_idTokenPayload && _idTokenPayload.exp) ? _idTokenPayload.exp: Number.MAX_VALUE;
54
+ const accessTokenExpiresAt = (accessTokenPayload && accessTokenPayload.exp)? accessTokenPayload.exp : tokens.issuedAt + tokens.expiresIn;
55
+ const expiresAt = idTokenExipreAt < accessTokenExpiresAt ? idTokenExipreAt : accessTokenExpiresAt;
56
+
57
+ return {...tokens, idTokenPayload: _idTokenPayload, accessTokenPayload, expiresAt};
58
+ }
59
+
60
+
61
+ export const parseOriginalTokens= (tokens) =>{
62
+ if(!tokens){
63
+ return null;
64
+ }
65
+ if(!tokens.issued_at) {
66
+ const currentTimeUnixSecond = new Date().getTime() /1000;
67
+ tokens.issued_at = currentTimeUnixSecond;
68
+ }
69
+
70
+ const data = {
71
+ accessToken: tokens.access_token,
72
+ expiresIn: tokens.expires_in,
73
+ idToken: tokens.id_token,
74
+ refreshToken: tokens.refresh_token,
75
+ scope: tokens.scope,
76
+ tokenType: tokens.token_type,
77
+ issuedAt: tokens.issued_at
78
+ };
79
+
80
+
81
+ if(tokens.accessTokenPayload !== undefined){
82
+ // @ts-ignore
83
+ data.accessTokenPayload = tokens.accessTokenPayload;
84
+ }
85
+
86
+ if(tokens.idTokenPayload !== undefined){
87
+ // @ts-ignore
88
+ data.idTokenPayload = tokens.idTokenPayload;
89
+ }
90
+
91
+ return setTokens(data);
92
+ }
93
+
94
+ export const computeTimeLeft = (refreshTimeBeforeTokensExpirationInSecond, expiresAt)=>{
95
+ const currentTimeUnixSecond = new Date().getTime() /1000;
96
+ return Math.round(((expiresAt - refreshTimeBeforeTokensExpirationInSecond) - currentTimeUnixSecond));
97
+ }
98
+
99
+ export const isTokensValid= (tokens) =>{
100
+ if(!tokens){
101
+ return false;
102
+ }
103
+ return computeTimeLeft(0, tokens.expiresAt) > 0;
104
+ }