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

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
-
51
+ console.log(tokens);
71
52
  return { success : true,
72
- data
53
+ data: parseOriginalTokens(tokens)
73
54
  };
74
55
  }
75
56
 
@@ -115,36 +96,6 @@ export class OidcAuthorizationServiceConfiguration extends AuthorizationServiceC
115
96
 
116
97
  }
117
98
 
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
99
 
149
100
  export interface StringMap {
150
101
  [key: string]: string;
@@ -200,34 +151,41 @@ const loginCallbackWithAutoTokensRenewAsync = async (oidc) => {
200
151
  }
201
152
 
202
153
  const autoRenewTokens = (oidc, refreshToken, expiresAt) => {
203
- const refreshTimeBeforeTokensExpirationInSecond = oidc.configuration.refresh_time_before_tokens_expiration_in_second ?? 60;
154
+ const refreshTimeBeforeTokensExpirationInSecond = oidc.configuration.refresh_time_before_tokens_expiration_in_second;
204
155
  return timer.setTimeout(async () => {
205
- const currentTimeUnixSecond = new Date().getTime() /1000;
206
- const timeInfo = { timeLeft: Math.round(((expiresAt - refreshTimeBeforeTokensExpirationInSecond) - currentTimeUnixSecond))};
156
+ const timeLeft = computeTimeLeft(refreshTimeBeforeTokensExpirationInSecond, expiresAt);
157
+ const timeInfo = { timeLeft };
207
158
  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);
159
+ // if(timeLeft <= 0) {
160
+ const tokens = await oidc.synchroniseTokensAsync(refreshToken);
161
+ oidc.tokens= tokens;
211
162
  if(!oidc.serviceWorker){
212
163
  await oidc.session.setTokens(oidc.tokens);
213
164
  }
214
165
  if(!oidc.tokens){
215
- if(oidc.checkSessionIFrame){
216
- oidc.checkSessionIFrame.stop();
217
- oidc.checkSessionIFrame = null;
218
- }
166
+ await oidc.destroyAsync();
167
+ oidc.publishEvent(eventNames.refreshTokensAsync_error, {message: `refresh token` });
219
168
  return;
220
169
  }
221
- oidc.publishEvent(Oidc.eventNames.token_renewed, {});
170
+
222
171
  if(oidc.timeoutId) {
223
172
  oidc.timeoutId = autoRenewTokens(oidc, tokens.refreshToken, oidc.tokens.expiresAt);
224
173
  }
225
- } else{
174
+ /*} else{
226
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
+ }
227
185
  if(tokens && oidc.timeoutId) {
228
186
  oidc.timeoutId = autoRenewTokens(oidc, tokens.refreshToken, tokens.expiresAt);
229
187
  }
230
- }
188
+ }*/
231
189
  }, 1000);
232
190
  }
233
191
 
@@ -245,11 +203,15 @@ const userInfoAsync = async (oidc) => {
245
203
  if(!oidc.tokens){
246
204
  return null;
247
205
  }
248
- if(oidc.syncTokensAsyncPromise){
249
- await oidc.syncTokensAsyncPromise;
250
- }
251
206
  const accessToken = oidc.tokens.accessToken;
252
-
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
+
253
215
  const oidcServerConfiguration = await oidc.initAsync(oidc.configuration.authority, oidc.configuration.authority_configuration);
254
216
  const url = oidcServerConfiguration.userInfoEndpoint;
255
217
  const fetchUserInfo = async (accessToken) => {
@@ -271,28 +233,6 @@ const userInfoAsync = async (oidc) => {
271
233
  return userInfo;
272
234
  }
273
235
 
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
236
  const eventNames = {
297
237
  service_worker_not_supported_by_browser: "service_worker_not_supported_by_browser",
298
238
  token_aquired: "token_aquired",
@@ -390,7 +330,12 @@ export class Oidc {
390
330
  silent_login_uri = `${configuration.silent_redirect_uri.replace("-callback", "").replace("callback", "")}-login`;
391
331
  }
392
332
 
393
- 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
+ };
394
339
  this.configurationName= configurationName;
395
340
  this.tokens = null
396
341
  this.userInfo = null;
@@ -398,7 +343,7 @@ export class Oidc {
398
343
  this.timeoutId = null;
399
344
  this.serviceWorker = null;
400
345
  this.session = null;
401
- this.refreshTokensAsync.bind(this);
346
+ this.synchroniseTokensAsync.bind(this);
402
347
  this.loginCallbackWithAutoTokensRenewAsync.bind(this);
403
348
  this.initAsync.bind(this);
404
349
  this.loginCallbackAsync.bind(this);
@@ -468,17 +413,6 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
468
413
  if (!this.configuration.silent_redirect_uri || !this.configuration.silent_login_uri) {
469
414
  return Promise.resolve(null);
470
415
  }
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
416
 
483
417
  try {
484
418
  this.publishEvent(eventNames.silentLoginAsync_begin, {});
@@ -549,7 +483,7 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
549
483
  }
550
484
  }
551
485
  };
552
- const silentSigninTimeout = configuration.silent_login_timeout ?? 12000
486
+ const silentSigninTimeout = configuration.silent_login_timeout;
553
487
  setTimeout(() => {
554
488
  if (!isResolved) {
555
489
  self.publishEvent(eventNames.silentLoginAsync_error, {reason: "timeout"});
@@ -583,8 +517,7 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
583
517
 
584
518
  const serviceWorker = await initWorkerAsync(this.configuration.service_worker_relative_url, this.configurationName);
585
519
  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;
520
+ return await fetchFromIssuer(authority, this.configuration.authority_time_cache_wellknowurl_in_second ?? 60 * 60, storage);
588
521
  }
589
522
 
590
523
  tryKeepExistingSessionPromise = null;
@@ -608,16 +541,7 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
608
541
  if (tokens) {
609
542
  serviceWorker.startKeepAliveServiceWorker();
610
543
  // @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);
544
+ this.tokens = tokens;
621
545
  this.serviceWorker = serviceWorker;
622
546
  // @ts-ignore
623
547
  this.timeoutId = autoRenewTokens(this, this.tokens.refreshToken, this.tokens.expiresAt);
@@ -644,7 +568,7 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
644
568
  const {tokens} = await session.initAsync();
645
569
  if (tokens) {
646
570
  // @ts-ignore
647
- this.tokens = await setTokensAsync(serviceWorker, tokens);
571
+ this.tokens = setTokens(tokens);
648
572
  //session.setTokens(this.tokens);
649
573
  this.session = session;
650
574
  // @ts-ignore
@@ -682,7 +606,7 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
682
606
  }
683
607
 
684
608
  loginPromise: Promise<any>=null;
685
- 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) {
686
610
  if(this.loginPromise !== null){
687
611
  return this.loginPromise;
688
612
  }
@@ -808,7 +732,7 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
808
732
  const response = await this._loginCallbackAsync(isSilenSignin);
809
733
  // @ts-ignore
810
734
  const tokens = response.tokens;
811
- const parsedTokens = await setTokensAsync(this.serviceWorker, tokens);
735
+ const parsedTokens = setTokens(tokens);
812
736
  this.tokens = parsedTokens;
813
737
  if(!this.serviceWorker){
814
738
  await this.session.setTokens(parsedTokens);
@@ -943,158 +867,140 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
943
867
  }
944
868
  }
945
869
 
946
- async refreshTokensAsync(refreshToken) {
870
+ async synchroniseTokensAsync(refreshToken, index=0) {
947
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;
948
885
  const localsilentLoginAsync= async () => {
949
886
  try {
950
887
  const loginParams = getLoginParams(this.configurationName, configuration.redirect_uri);
951
- 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);
952
892
  if (silent_token_response) {
893
+ this.publishEvent(Oidc.eventNames.token_renewed, {});
953
894
  return silent_token_response.tokens;
954
895
  }
955
896
  } catch (exceptionSilent) {
956
- console.error(exceptionSilent);
957
- }
958
- if(this.timeoutId){
959
- timer.clearTimeout(this.timeoutId);
960
- this.timeoutId=null;
897
+ if(exceptionSilent && exceptionSilent.message && exceptionSilent.message.startsWith("oidc")){
898
+ return null;
899
+ }
900
+ throw exceptionSilent;
961
901
  }
962
- this.publishEvent(eventNames.refreshTokensAsync_error, {message: "refresh token and silent refresh failed"});
963
902
  return null;
964
903
  }
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
904
 
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
-
991
- let index = 0;
992
- while (index <=4) {
905
+ if (index <=4) {
993
906
  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});
907
+
908
+ if(!refreshToken)
909
+ {
910
+ this.publishEvent(eventNames.refreshTokensAsync_begin, {refreshToken:refreshToken, tryNumber: index});
1013
911
  return await localsilentLoginAsync();
1014
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
+ }
1015
958
  } catch (exception) {
1016
959
  console.error(exception);
1017
960
  this.publishEvent(eventNames.refreshTokensAsync_silent_error, {message: "exception" ,exception: exception.message});
961
+ return this.synchroniseTokensAsync(refreshToken, index+1);
1018
962
  }
1019
- index++;
1020
963
  }
1021
-
964
+ return null;
1022
965
  }
1023
966
 
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
- }
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
+ }
1033
973
 
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;
1078
- }
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 };
1079
996
  }
1080
- return this.tokens;
1081
- }
1082
-
1083
- if(this.syncTokensAsyncPromise){
1084
- return this.syncTokensAsyncPromise;
1085
997
  }
1086
998
 
1087
- this.syncTokensAsyncPromise = localSyncTokensAsync().then(result =>{
1088
- if(this.syncTokensAsyncPromise){
1089
- this.syncTokensAsyncPromise = null;
1090
- }
1091
- return result;
1092
- });
1093
-
1094
- 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};
1095
1002
  }
1096
1003
 
1097
-
1098
1004
  loginCallbackWithAutoTokensRenewPromise:Promise<loginCallbackResult> = null;
1099
1005
  loginCallbackWithAutoTokensRenewAsync():Promise<loginCallbackResult>{
1100
1006
  if(this.loginCallbackWithAutoTokensRenewPromise !== null){
@@ -1138,7 +1044,6 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
1138
1044
 
1139
1045
  async logoutOtherTabAsync(sub){
1140
1046
  // @ts-ignore
1141
-
1142
1047
  if(this.configuration.monitor_session && sub && this.tokens && this.tokens.idTokenPayload && this.tokens.idTokenPayload.sub === sub) {
1143
1048
  await this.destroyAsync();
1144
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
+ }