@axa-fr/oidc-client 7.26.7 → 7.27.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.
@@ -1,7 +1,19 @@
1
- export const initSession = (configurationName, storage = sessionStorage) => {
1
+ export const initSession = (
2
+ configurationName,
3
+ storage = sessionStorage,
4
+ loginStateStorage?: Storage,
5
+ ) => {
6
+ const loginStorage = loginStateStorage ?? storage;
7
+
2
8
  const clearAsync = status => {
3
9
  storage[`oidc.${configurationName}`] = JSON.stringify({ tokens: null, status });
4
10
  delete storage[`oidc.${configurationName}.userInfo`];
11
+ if (loginStateStorage && loginStateStorage !== storage) {
12
+ delete loginStorage[`oidc.login.${configurationName}`];
13
+ delete loginStorage[`oidc.state.${configurationName}`];
14
+ delete loginStorage[`oidc.code_verifier.${configurationName}`];
15
+ delete loginStorage[`oidc.nonce.${configurationName}`];
16
+ }
5
17
  return Promise.resolve();
6
18
  };
7
19
 
@@ -27,7 +39,7 @@ export const initSession = (configurationName, storage = sessionStorage) => {
27
39
  };
28
40
 
29
41
  const setNonceAsync = nonce => {
30
- storage[`oidc.nonce.${configurationName}`] = nonce.nonce;
42
+ loginStorage[`oidc.nonce.${configurationName}`] = nonce.nonce;
31
43
  };
32
44
 
33
45
  const setDemonstratingProofOfPossessionJwkAsync = (jwk: JsonWebKey) => {
@@ -40,7 +52,7 @@ export const initSession = (configurationName, storage = sessionStorage) => {
40
52
 
41
53
  const getNonceAsync = async () => {
42
54
  // @ts-ignore
43
- return { nonce: storage[`oidc.nonce.${configurationName}`] };
55
+ return { nonce: loginStorage[`oidc.nonce.${configurationName}`] };
44
56
  };
45
57
 
46
58
  const setDemonstratingProofOfPossessionNonce = async (dpopNonce: string) => {
@@ -61,10 +73,10 @@ export const initSession = (configurationName, storage = sessionStorage) => {
61
73
  const getLoginParamsCache = {};
62
74
  const setLoginParams = data => {
63
75
  getLoginParamsCache[configurationName] = data;
64
- storage[`oidc.login.${configurationName}`] = JSON.stringify(data);
76
+ loginStorage[`oidc.login.${configurationName}`] = JSON.stringify(data);
65
77
  };
66
78
  const getLoginParams = () => {
67
- const dataString = storage[`oidc.login.${configurationName}`];
79
+ const dataString = loginStorage[`oidc.login.${configurationName}`];
68
80
 
69
81
  if (!dataString) {
70
82
  console.warn(
@@ -80,19 +92,19 @@ export const initSession = (configurationName, storage = sessionStorage) => {
80
92
  };
81
93
 
82
94
  const getStateAsync = async () => {
83
- return storage[`oidc.state.${configurationName}`];
95
+ return loginStorage[`oidc.state.${configurationName}`];
84
96
  };
85
97
 
86
98
  const setStateAsync = async (state: string) => {
87
- storage[`oidc.state.${configurationName}`] = state;
99
+ loginStorage[`oidc.state.${configurationName}`] = state;
88
100
  };
89
101
 
90
102
  const getCodeVerifierAsync = async () => {
91
- return storage[`oidc.code_verifier.${configurationName}`];
103
+ return loginStorage[`oidc.code_verifier.${configurationName}`];
92
104
  };
93
105
 
94
106
  const setCodeVerifierAsync = async codeVerifier => {
95
- storage[`oidc.code_verifier.${configurationName}`] = codeVerifier;
107
+ loginStorage[`oidc.code_verifier.${configurationName}`] = codeVerifier;
96
108
  };
97
109
 
98
110
  return {
package/src/initWorker.ts CHANGED
@@ -181,38 +181,56 @@ export const initWorkerAsync = async (
181
181
  });
182
182
  }
183
183
 
184
- // (Optional but useful on Safari) ask for update early
185
- try {
186
- await registration.update();
187
- } catch (ex) {
188
- console.error(ex);
189
- }
184
+ const versionMismatchKey = `oidc.sw.version_mismatch_reload.${configurationName}`;
190
185
 
191
- // 1) Détection updatefound
192
- registration.addEventListener('updatefound', () => {
193
- const newSW = registration.installing;
186
+ const sendSkipWaiting = async () => {
194
187
  stopKeepAlive();
188
+ console.log('New SW waiting – SKIP_WAITING');
189
+ try {
190
+ await sendMessageAsync(registration, { timeoutMs: 8000 })({
191
+ type: 'SKIP_WAITING',
192
+ configurationName,
193
+ data: null,
194
+ });
195
+ } catch (e) {
196
+ console.warn('SKIP_WAITING failed', e);
197
+ }
198
+ };
195
199
 
196
- newSW?.addEventListener('statechange', async () => {
200
+ const trackInstallingWorker = (newSW: ServiceWorker) => {
201
+ stopKeepAlive();
202
+ newSW.addEventListener('statechange', async () => {
197
203
  if (newSW.state === 'installed' && navigator.serviceWorker.controller) {
198
- stopKeepAlive();
199
- console.log('New SW waiting – SKIP_WAITING');
200
-
201
- try {
202
- // Use MessageChannel to avoid “fire and forget” hangs
203
- await sendMessageAsync(registration, { timeoutMs: 8000 })({
204
- type: 'SKIP_WAITING',
205
- configurationName,
206
- data: null,
207
- });
208
- } catch (e) {
209
- console.warn('SKIP_WAITING failed', e);
210
- }
204
+ await sendSkipWaiting();
211
205
  }
212
206
  });
207
+ };
208
+
209
+ // 1) Détection updatefound – registered BEFORE update() to avoid missing the event
210
+ registration.addEventListener('updatefound', () => {
211
+ const newSW = registration.installing;
212
+ if (newSW) {
213
+ trackInstallingWorker(newSW);
214
+ }
213
215
  });
214
216
 
215
- // 2) Quand le SW actif change, on reload (once)
217
+ // Handle a SW that is already installing or waiting (e.g. when the listener above was
218
+ // registered after the updatefound event already fired in a previous call)
219
+ if (registration.installing) {
220
+ trackInstallingWorker(registration.installing);
221
+ } else if (registration.waiting && navigator.serviceWorker.controller) {
222
+ // A new SW is already waiting – activate it straight away
223
+ sendSkipWaiting();
224
+ }
225
+
226
+ // (Optional but useful on Safari) ask for update early
227
+ try {
228
+ await registration.update();
229
+ } catch (ex) {
230
+ console.error(ex);
231
+ }
232
+
233
+ // 2) Quand le SW actif change, on reload (once per session)
216
234
  const reloadKey = `oidc.sw.controllerchange.reloaded.${configurationName}`;
217
235
  navigator.serviceWorker.addEventListener('controllerchange', () => {
218
236
  try {
@@ -275,6 +293,35 @@ export const initWorkerAsync = async (
275
293
  console.warn(
276
294
  `Service worker ${serviceWorkerVersion} version mismatch with js client version ${codeVersion}, unregistering and reloading`,
277
295
  );
296
+
297
+ const reloadCount = parseInt(sessionStorage.getItem(versionMismatchKey) ?? '0', 10);
298
+ if (reloadCount < 3) {
299
+ sessionStorage.setItem(versionMismatchKey, String(reloadCount + 1));
300
+ // If a new SW is already waiting, skip it into activation so controllerchange triggers reload
301
+ if (registration.waiting) {
302
+ await sendSkipWaiting();
303
+ } else {
304
+ // No waiting SW – force a fresh update and reload
305
+ stopKeepAlive();
306
+ try {
307
+ await registration.update();
308
+ } catch (ex) {
309
+ console.error(ex);
310
+ }
311
+ const isSuccess = await registration.unregister();
312
+ console.log(`Service worker unregistering ${isSuccess}`);
313
+ await sleepAsync({ milliseconds: 2000 });
314
+ window.location.reload();
315
+ }
316
+ } else {
317
+ console.error(
318
+ `Service worker version mismatch persists after ${reloadCount} attempt(s). Continuing with mismatched version.`,
319
+ );
320
+ sessionStorage.removeItem(versionMismatchKey);
321
+ }
322
+ } else {
323
+ // Version matches – clear any leftover mismatch counter
324
+ sessionStorage.removeItem(versionMismatchKey);
278
325
  }
279
326
 
280
327
  // @ts-ignore
@@ -1,4 +1,4 @@
1
- import { eventNames } from './events';
1
+ import { eventNames } from './events';
2
2
  import { initSession } from './initSession';
3
3
  import { initWorkerAsync } from './initWorker';
4
4
  import Oidc from './oidc';
@@ -62,7 +62,11 @@ export const tryKeepSessionAsync = async (oidc: Oidc) => {
62
62
  message: 'service worker is not supported by this browser',
63
63
  });
64
64
  }
65
- const session = initSession(oidc.configurationName, configuration.storage ?? sessionStorage);
65
+ const session = initSession(
66
+ oidc.configurationName,
67
+ configuration.storage ?? sessionStorage,
68
+ configuration.login_state_storage ?? configuration.storage ?? sessionStorage,
69
+ );
66
70
  const { tokens } = await session.initAsync();
67
71
  if (tokens) {
68
72
  // @ts-ignore
package/src/login.ts CHANGED
@@ -69,7 +69,11 @@ export const defaultLoginAsync =
69
69
  serviceWorker.startKeepAliveServiceWorker();
70
70
  storage = serviceWorker;
71
71
  } else {
72
- const session = initSession(configurationName, configuration.storage ?? sessionStorage);
72
+ const session = initSession(
73
+ configurationName,
74
+ configuration.storage ?? sessionStorage,
75
+ configuration.login_state_storage ?? configuration.storage ?? sessionStorage,
76
+ );
73
77
  session.setLoginParams({ callbackPath: url, extras: originExtras, scope: scope });
74
78
  await session.setNonceAsync(nonce);
75
79
  storage = session;
@@ -131,6 +135,7 @@ export const loginCallbackAsync =
131
135
  const session = initSession(
132
136
  oidc.configurationName,
133
137
  configuration.storage ?? sessionStorage,
138
+ configuration.login_state_storage ?? configuration.storage ?? sessionStorage,
134
139
  );
135
140
  await session.setSessionStateAsync(sessionState);
136
141
  nonceData = await session.getNonceAsync();
@@ -186,7 +191,11 @@ export const loginCallbackAsync =
186
191
  const jwk = await generateJwkAsync(window)(
187
192
  configuration.demonstrating_proof_of_possession_configuration.generateKeyAlgorithm,
188
193
  );
189
- const session = initSession(oidc.configurationName, configuration.storage);
194
+ const session = initSession(
195
+ oidc.configurationName,
196
+ configuration.storage,
197
+ configuration.login_state_storage ?? configuration.storage,
198
+ );
190
199
  await session.setDemonstratingProofOfPossessionJwkAsync(jwk);
191
200
  headersExtras['DPoP'] = await generateJwtDemonstratingProofOfPossessionAsync(window)(
192
201
  configuration.demonstrating_proof_of_possession_configuration,
@@ -251,7 +260,11 @@ export const loginCallbackAsync =
251
260
  );
252
261
  }
253
262
  } else {
254
- const session = initSession(oidc.configurationName, configuration.storage);
263
+ const session = initSession(
264
+ oidc.configurationName,
265
+ configuration.storage,
266
+ configuration.login_state_storage ?? configuration.storage,
267
+ );
255
268
  loginParams = session.getLoginParams();
256
269
  if (demonstratingProofOfPossessionNonce) {
257
270
  await session.setDemonstratingProofOfPossessionNonce(demonstratingProofOfPossessionNonce);
package/src/logout.ts CHANGED
@@ -46,7 +46,11 @@ export const destroyAsync = oidc => async status => {
46
46
  }
47
47
  const serviceWorker = await initWorkerAsync(oidc.configuration, oidc.configurationName);
48
48
  if (!serviceWorker) {
49
- const session = initSession(oidc.configurationName, oidc.configuration.storage);
49
+ const session = initSession(
50
+ oidc.configurationName,
51
+ oidc.configuration.storage,
52
+ oidc.configuration.login_state_storage ?? oidc.configuration.storage,
53
+ );
50
54
  await session.clearAsync(status);
51
55
  } else {
52
56
  await serviceWorker.clearAsync(status);
package/src/oidc.ts CHANGED
@@ -348,7 +348,11 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
348
348
  this.tokens = parsedTokens;
349
349
  const serviceWorker = await initWorkerAsync(this.configuration, this.configurationName);
350
350
  if (!serviceWorker) {
351
- const session = initSession(this.configurationName, this.configuration.storage);
351
+ const session = initSession(
352
+ this.configurationName,
353
+ this.configuration.storage,
354
+ this.configuration.login_state_storage ?? this.configuration.storage,
355
+ );
352
356
  session.setTokens(parsedTokens);
353
357
  }
354
358
  this.publishEvent(Oidc.eventNames.token_acquired, parsedTokens);
@@ -388,7 +392,11 @@ Please checkout that you are using OIDC hook inside a <OidcProvider configuratio
388
392
  return `DPOP_SECURED_BY_OIDC_SERVICE_WORKER_${this.configurationName}#tabId=${getTabId(this.configurationName)}`;
389
393
  }
390
394
 
391
- const session = initSession(this.configurationName, configuration.storage);
395
+ const session = initSession(
396
+ this.configurationName,
397
+ configuration.storage,
398
+ configuration.login_state_storage ?? configuration.storage,
399
+ );
392
400
  const jwk = await session.getDemonstratingProofOfPossessionJwkAsync();
393
401
  const demonstratingProofOfPossessionNonce = session.getDemonstratingProofOfPossessionNonce();
394
402
 
@@ -28,7 +28,11 @@ async function syncTokens(
28
28
 
29
29
  const serviceWorker = await initWorkerAsync(oidc.configuration, oidc.configurationName);
30
30
  if (!serviceWorker) {
31
- const session = initSession(oidc.configurationName, oidc.configuration.storage);
31
+ const session = initSession(
32
+ oidc.configurationName,
33
+ oidc.configuration.storage,
34
+ oidc.configuration.login_state_storage ?? oidc.configuration.storage,
35
+ );
32
36
  session.setTokens(oidc.tokens);
33
37
  }
34
38
 
@@ -169,7 +173,11 @@ export const syncTokensInfoAsync =
169
173
  }
170
174
  nonce = await serviceWorker.getNonceAsync();
171
175
  } else {
172
- const session = initSession(configurationName, configuration.storage ?? sessionStorage);
176
+ const session = initSession(
177
+ configurationName,
178
+ configuration.storage ?? sessionStorage,
179
+ configuration.login_state_storage ?? configuration.storage ?? sessionStorage,
180
+ );
173
181
  const initAsyncResponse = await session.initAsync();
174
182
  let { tokens } = initAsyncResponse;
175
183
  const { status } = initAsyncResponse;
@@ -263,7 +271,11 @@ const synchroniseTokensAsync =
263
271
  if (serviceWorker) {
264
272
  loginParams = serviceWorker.getLoginParams();
265
273
  } else {
266
- const session = initSession(oidc.configurationName, configuration.storage);
274
+ const session = initSession(
275
+ oidc.configurationName,
276
+ configuration.storage,
277
+ configuration.login_state_storage ?? configuration.storage,
278
+ );
267
279
  loginParams = session.getLoginParams();
268
280
  }
269
281
  const silentLoginInput = {};
@@ -454,7 +466,11 @@ const synchroniseTokensAsync =
454
466
  tokenResponse.demonstratingProofOfPossessionNonce,
455
467
  );
456
468
  } else {
457
- const session = initSession(oidc.configurationName, configuration.storage);
469
+ const session = initSession(
470
+ oidc.configurationName,
471
+ configuration.storage,
472
+ configuration.login_state_storage ?? configuration.storage,
473
+ );
458
474
  await session.setDemonstratingProofOfPossessionNonce(
459
475
  tokenResponse.demonstratingProofOfPossessionNonce,
460
476
  );
package/src/types.ts CHANGED
@@ -39,6 +39,7 @@ export type OidcConfiguration = {
39
39
  extras?: StringMap;
40
40
  token_request_extras?: StringMap;
41
41
  storage?: Storage;
42
+ login_state_storage?: Storage;
42
43
  monitor_session?: boolean;
43
44
  token_renew_mode?: string;
44
45
  logout_tokens_to_invalidate?: Array<LogoutToken>;
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export default '7.26.7';
1
+ export default '7.27.0';