@schibsted/account-sdk-browser 5.0.0 → 5.0.1-beta

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@schibsted/account-sdk-browser",
3
- "version": "5.0.0",
3
+ "version": "5.0.1-beta",
4
4
  "description": "Schibsted account SDK for browsers",
5
5
  "main": "index.js",
6
6
  "type": "module",
package/src/cache.d.ts CHANGED
@@ -14,7 +14,6 @@ export default class Cache {
14
14
  /**
15
15
  * Get a value from cache (checks that the object has not expired)
16
16
  * @param {string} key
17
- * @private
18
17
  * @returns {*} - The value if it exists, otherwise null
19
18
  */
20
19
  private get;
@@ -23,14 +22,12 @@ export default class Cache {
23
22
  * @param {string} key
24
23
  * @param {*} value
25
24
  * @param {Number} expiresIn - Value in milliseconds until the entry expires
26
- * @private
27
25
  * @returns {void}
28
26
  */
29
27
  private set;
30
28
  /**
31
29
  * Delete a cache entry
32
30
  * @param {string} key
33
- * @private
34
31
  * @returns {void}
35
32
  */
36
33
  private delete;
package/src/cache.js CHANGED
@@ -89,7 +89,6 @@ export default class Cache {
89
89
  /**
90
90
  * Get a value from cache (checks that the object has not expired)
91
91
  * @param {string} key
92
- * @private
93
92
  * @returns {*} - The value if it exists, otherwise null
94
93
  */
95
94
  get(key) {
@@ -124,7 +123,6 @@ export default class Cache {
124
123
  * @param {string} key
125
124
  * @param {*} value
126
125
  * @param {Number} expiresIn - Value in milliseconds until the entry expires
127
- * @private
128
126
  * @returns {void}
129
127
  */
130
128
  set(key, value, expiresIn = 0) {
@@ -145,7 +143,6 @@ export default class Cache {
145
143
  /**
146
144
  * Delete a cache entry
147
145
  * @param {string} key
148
- * @private
149
146
  * @returns {void}
150
147
  */
151
148
  delete(key) {
package/src/identity.d.ts CHANGED
@@ -28,7 +28,8 @@ export class Identity extends TinyEmitter {
28
28
  _sessionInitiatedSent: boolean;
29
29
  window: any;
30
30
  clientId: string;
31
- cache: any;
31
+ sessionStorageCache: any;
32
+ localStorageCache: any;
32
33
  redirectUri: string;
33
34
  env: string;
34
35
  log: Function;
@@ -36,6 +37,31 @@ export class Identity extends TinyEmitter {
36
37
  _sessionDomain: string;
37
38
  _enableSessionCaching: boolean;
38
39
  _session: {};
40
+
41
+ /**
42
+ * Read tabId from session storage
43
+ * @returns {number}
44
+ * @private
45
+ */
46
+ private _getTabId;
47
+ /**
48
+ * Checks if calling get session is blocked
49
+ * @private
50
+ * @returns {boolean|void}
51
+ */
52
+ private _isSessionCallBlocked;
53
+ /**
54
+ * Block calls to get session. This is done to prevent concurrent calls which can log user out if session is refreshed by one of them
55
+ * @private
56
+ * @returns {void}
57
+ */
58
+ private _blockSessionCall;
59
+ /**
60
+ * Unblocks calls to get session if the lock was put by the same tab
61
+ * @private
62
+ * @returns {void}
63
+ */
64
+ private _unblockSessionCall;
39
65
  /**
40
66
  * Set SPiD server URL
41
67
  * @private
@@ -77,7 +103,7 @@ export class Identity extends TinyEmitter {
77
103
  private _setGlobalSessionServiceUrl;
78
104
  _globalSessionService: RESTClient;
79
105
  /**
80
- * Emits the relevant events based on the previous and new reply from hassession
106
+ * Emits the relevant events based on the previous and new reply from {@link Identity#hasSession}
81
107
  * @private
82
108
  * @param {object} previous
83
109
  * @param {object} current
@@ -92,7 +118,7 @@ export class Identity extends TinyEmitter {
92
118
  private _closePopup;
93
119
  popup: Window;
94
120
  /**
95
- * Set the Varnish cookie (`SP_ID`) when hasSession() is called. Note that most browsers require
121
+ * Set the Varnish cookie (`SP_ID`) when {@link Identity#hasSession} is called. Note that most browsers require
96
122
  * that you are on a "real domain" for this to work — so, **not** `localhost`
97
123
  * @param {object} [options]
98
124
  * @param {number} [options.expiresIn] Override this to set number of seconds before the varnish
@@ -195,7 +221,7 @@ export class Identity extends TinyEmitter {
195
221
  * @description This function calls {@link Identity#hasSession} internally and thus has the side
196
222
  * effect that it might perform an auto-login on the user
197
223
  * @throws {SDKError} If the user isn't connected to the merchant
198
- * @return {Promise<string>} The `userId` field (not to be confused with the `uuid`)
224
+ * @return {number} The `userId` field (not to be confused with the `uuid`)
199
225
  */
200
226
  getUserId(): Promise<string>;
201
227
  /**
@@ -355,7 +381,7 @@ export type LoginOptions = {
355
381
  * `password` (will force password confirmation, even if user is already logged in), `eid`. Those values might
356
382
  * be mixed as space-separated string. To make sure that user has authenticated with 2FA you need
357
383
  * to verify AMR (Authentication Methods References) claim in ID token.
358
- * Might also be used to ensure additional acr (sms, otp, eid) for already logged in users.
384
+ * Might also be used to ensure additional acr (sms, otp, eid) for already logged-in users.
359
385
  * Supported value is also 'otp-email' means one time password using email.
360
386
  */
361
387
  acrValues?: string;
@@ -424,7 +450,7 @@ export type SimplifiedLoginWidgetLoginOptions = {
424
450
  * `password` (will force password confirmation, even if user is already logged in). Those values might
425
451
  * be mixed as space-separated string. To make sure that user has authenticated with 2FA you need
426
452
  * to verify AMR (Authentication Methods References) claim in ID token.
427
- * Might also be used to ensure additional acr (sms, otp) for already logged in users.
453
+ * Might also be used to ensure additional acr (sms, otp) for already logged-in users.
428
454
  * Supported value is also 'otp-email' means one time password using email.
429
455
  */
430
456
  acrValues?: string;
@@ -592,7 +618,7 @@ export type HasSessionFailureResponse = {
592
618
  };
593
619
  export type SimplifiedLoginData = {
594
620
  /**
595
- * - Deprecated: User UUID, to be be used as `loginHint` for {@link Identity#login}
621
+ * - Deprecated: User UUID, to be used as `loginHint` for {@link Identity#login}
596
622
  */
597
623
  identifier: string;
598
624
  /**
package/src/identity.js CHANGED
@@ -26,7 +26,7 @@ import version from './version.js';
26
26
  * `password` (will force password confirmation, even if user is already logged in), `eid`. Those values might
27
27
  * be mixed as space-separated string. To make sure that user has authenticated with 2FA you need
28
28
  * to verify AMR (Authentication Methods References) claim in ID token.
29
- * Might also be used to ensure additional acr (sms, otp) for already logged in users.
29
+ * Might also be used to ensure additional acr (sms, otp) for already logged-in users.
30
30
  * Supported value is also 'otp-email' means one time password using email.
31
31
  * @property {string} [scope] - The OAuth scopes for the tokens. This is a list of
32
32
  * scopes, separated by space. If the list of scopes contains `openid`, the generated tokens
@@ -60,7 +60,7 @@ import version from './version.js';
60
60
  * `password` (will force password confirmation, even if user is already logged in). Those values might
61
61
  * be mixed as space-separated string. To make sure that user has authenticated with 2FA you need
62
62
  * to verify AMR (Authentication Methods References) claim in ID token.
63
- * Might also be used to ensure additional acr (sms, otp) for already logged in users.
63
+ * Might also be used to ensure additional acr (sms, otp) for already logged-in users.
64
64
  * Supported value is also 'otp-email' means one time password using email.
65
65
  * @property {string} [scope] - The OAuth scopes for the tokens. This is a list of
66
66
  * scopes, separated by space. If the list of scopes contains `openid`, the generated tokens
@@ -134,7 +134,7 @@ import version from './version.js';
134
134
 
135
135
  /**
136
136
  * @typedef {object} SimplifiedLoginData
137
- * @property {string} identifier - Deprecated: User UUID, to be be used as `loginHint` for {@link Identity#login}
137
+ * @property {string} identifier - Deprecated: User UUID, to be as `loginHint` for {@link Identity#login}
138
138
  * @property {string} display_text - Human-readable user identifier
139
139
  * @property {string} client_name - Client name
140
140
  */
@@ -146,12 +146,16 @@ import version from './version.js';
146
146
 
147
147
  const HAS_SESSION_CACHE_KEY = 'hasSession-cache';
148
148
  const SESSION_CALL_BLOCKED_CACHE_KEY = 'sessionCallBlocked-cache';
149
- const SESSION_CALL_BLOCKED_TTL = 1000 * 60 * 5;
149
+ const SESSION_CALL_BLOCKED_TTL = 1000 * 30;
150
+
151
+ const TAB_ID_KEY = 'tab-id-cache';
152
+ const TAB_ID = Math.floor(Math.random() * 100000)
153
+ const TAB_ID_TTL = 1000 * 60 * 60 * 24 * 30;
150
154
 
151
155
  const globalWindow = () => window;
152
156
 
153
157
  /**
154
- * Provides Identity functionalty to a web page
158
+ * Provides Identity functionality to a web page
155
159
  */
156
160
  export class Identity extends EventEmitter {
157
161
  /**
@@ -182,18 +186,21 @@ export class Identity extends EventEmitter {
182
186
  assert(sessionDomain && isUrl(sessionDomain), 'sessionDomain parameter is not a valid URL');
183
187
 
184
188
  spidTalk.emulate(window);
189
+
190
+ // Internal hack: set as false to always refresh from hasSession
191
+ this._enableSessionCaching = true;
192
+
185
193
  this._sessionInitiatedSent = false;
186
194
  this.window = window;
187
195
  this.clientId = clientId;
188
- this.cache = new Cache(() => this.window && this.window.sessionStorage);
196
+ this.sessionStorageCache = new Cache(this.window && this.window.sessionStorage);
197
+ this.localStorageCache = new Cache(this.window && this.window.localStorage);
189
198
  this.redirectUri = redirectUri;
190
199
  this.env = env;
191
200
  this.log = log;
192
201
  this.callbackBeforeRedirect = callbackBeforeRedirect;
193
202
  this._sessionDomain = sessionDomain;
194
-
195
- // Internal hack: set to false to always refresh from hassession
196
- this._enableSessionCaching = true;
203
+ this._tabId = this._getTabId();
197
204
 
198
205
  // Old session
199
206
  this._session = {};
@@ -203,49 +210,59 @@ export class Identity extends EventEmitter {
203
210
  this._setBffServerUrl(env);
204
211
  this._setOauthServerUrl(env);
205
212
  this._setGlobalSessionServiceUrl(env);
206
-
207
- this._unblockSessionCall();
213
+ this._unblockSessionCallByTab();
208
214
  }
209
215
 
210
216
  /**
211
- * Checks if getting session is blocked
217
+ * Read tabId from session storage if possible, otherwise save tabId to session storage and return it
218
+ * @returns {number}
212
219
  * @private
213
- *
214
- * @returns {boolean|void}
215
220
  */
216
- _isSessionCallBlocked(){
221
+ _getTabId() {
217
222
  if (this._enableSessionCaching) {
218
- return this.cache.get(SESSION_CALL_BLOCKED_CACHE_KEY);
223
+ const tabId = this.sessionStorageCache.get(TAB_ID_KEY);
224
+ if (!tabId) {
225
+ this.sessionStorageCache.set(TAB_ID_KEY, TAB_ID, TAB_ID_TTL);
226
+
227
+ return TAB_ID;
228
+ }
229
+
230
+ return tabId;
219
231
  }
232
+
233
+ return TAB_ID;
220
234
  }
221
235
 
222
236
  /**
223
- * Block calls to get session
237
+ * Checks if calling GET session is blocked
238
+ * @private
239
+ * @returns {number|null}
240
+ */
241
+ _isSessionCallBlocked(){
242
+ return this.localStorageCache.get(SESSION_CALL_BLOCKED_CACHE_KEY);
243
+ }
244
+
245
+ /**
246
+ * Block calls to get session. This is done to prevent concurrent calls which can log user out if session is refreshed by one of them
224
247
  * @private
225
- *
226
248
  * @returns {void}
227
249
  */
228
250
  _blockSessionCall(){
229
- if (this._enableSessionCaching) {
230
- const SESSION_CALL_BLOCKED = true;
231
-
232
- this.cache.set(
233
- SESSION_CALL_BLOCKED_CACHE_KEY,
234
- SESSION_CALL_BLOCKED,
235
- SESSION_CALL_BLOCKED_TTL
236
- );
237
- }
251
+ this.localStorageCache.set(
252
+ SESSION_CALL_BLOCKED_CACHE_KEY,
253
+ this._tabId,
254
+ SESSION_CALL_BLOCKED_TTL
255
+ );
238
256
  }
239
257
 
240
258
  /**
241
- * Unblocks calls to get session
259
+ * Unblocks calls to get session if the lock was put by the same tab
242
260
  * @private
243
- *
244
261
  * @returns {void}
245
262
  */
246
- _unblockSessionCall(){
247
- if (this._enableSessionCaching) {
248
- this.cache.delete(SESSION_CALL_BLOCKED_CACHE_KEY);
263
+ _unblockSessionCallByTab() {
264
+ if (this._isSessionCallBlocked() === this._tabId) {
265
+ this.localStorageCache.delete(SESSION_CALL_BLOCKED_CACHE_KEY);
249
266
  }
250
267
  }
251
268
 
@@ -327,7 +344,7 @@ export class Identity extends EventEmitter {
327
344
  }
328
345
 
329
346
  /**
330
- * Emits the relevant events based on the previous and new reply from hassession
347
+ * Emits the relevant events based on the previous and new reply from {@link Identity#hasSession}
331
348
  * @private
332
349
  * @param {object} previous
333
350
  * @param {object} current
@@ -407,7 +424,7 @@ export class Identity extends EventEmitter {
407
424
  }
408
425
 
409
426
  /**
410
- * Set the Varnish cookie (`SP_ID`) when hasSession() is called. Note that most browsers require
427
+ * Set the Varnish cookie (`SP_ID`) when {@link Identity#hasSession} is called. Note that most browsers require
411
428
  * that you are on a "real domain" for this to work — so, **not** `localhost`
412
429
  * @param {object} [options]
413
430
  * @param {number} [options.expiresIn] Override this to set number of seconds before the varnish
@@ -566,18 +583,19 @@ export class Identity extends EventEmitter {
566
583
  const _getSession = async () => {
567
584
  if (this._enableSessionCaching) {
568
585
  // Try to resolve from cache (it has a TTL)
569
- let cachedSession = this.cache.get(HAS_SESSION_CACHE_KEY);
586
+ let cachedSession = this.sessionStorageCache.get(HAS_SESSION_CACHE_KEY);
570
587
  if (cachedSession) {
571
588
  return _postProcess(cachedSession);
572
589
  }
573
590
  }
591
+
574
592
  let sessionData = null;
575
593
  try {
576
- sessionData = await this._sessionService.get('/v2/session');
594
+ sessionData = await this._sessionService.get('/v2/session', {tabId: this._tabId});
577
595
  } catch (err) {
578
596
  if (err && err.code === 400 && this._enableSessionCaching) {
579
597
  const expiresIn = 1000 * (err.expiresIn || 300);
580
- this.cache.set(HAS_SESSION_CACHE_KEY, { error: err }, expiresIn);
598
+ this.sessionStorageCache.set(HAS_SESSION_CACHE_KEY, { error: err }, expiresIn);
581
599
  }
582
600
  throw err;
583
601
  }
@@ -589,12 +607,12 @@ export class Identity extends EventEmitter {
589
607
 
590
608
  await this.callbackBeforeRedirect();
591
609
 
592
- return this._sessionService.makeUrl(sessionData.redirectURL);
610
+ return this._sessionService.makeUrl(sessionData.redirectURL, {tabId: this._getTabId()});
593
611
  }
594
612
 
595
613
  if (this._enableSessionCaching) {
596
614
  const expiresIn = 1000 * (sessionData.expiresIn || 300);
597
- this.cache.set(HAS_SESSION_CACHE_KEY, sessionData, expiresIn);
615
+ this.sessionStorageCache.set(HAS_SESSION_CACHE_KEY, sessionData, expiresIn);
598
616
  }
599
617
  }
600
618
 
@@ -642,7 +660,7 @@ export class Identity extends EventEmitter {
642
660
  * @returns {void}
643
661
  */
644
662
  clearCachedUserSession() {
645
- this.cache.delete(HAS_SESSION_CACHE_KEY);
663
+ this.sessionStorageCache.delete(HAS_SESSION_CACHE_KEY);
646
664
  }
647
665
 
648
666
  /**
@@ -696,7 +714,7 @@ export class Identity extends EventEmitter {
696
714
  * @description This function calls {@link Identity#hasSession} internally and thus has the side
697
715
  * effect that it might perform an auto-login on the user
698
716
  * @throws {SDKError} If the user isn't connected to the merchant
699
- * @return {Promise<string>} The `userId` field (not to be confused with the `uuid`)
717
+ * @return {number} The `userId` field (not to be confused with the `uuid`)
700
718
  */
701
719
  async getUserId() {
702
720
  const user = await this.hasSession();
@@ -853,7 +871,7 @@ export class Identity extends EventEmitter {
853
871
  prompt = 'select_account'
854
872
  }) {
855
873
  this._closePopup();
856
- this.cache.delete(HAS_SESSION_CACHE_KEY);
874
+ this.sessionStorageCache.delete(HAS_SESSION_CACHE_KEY);
857
875
  const url = this.loginUrl({
858
876
  state,
859
877
  acrValues,
@@ -902,7 +920,7 @@ export class Identity extends EventEmitter {
902
920
  * @return {void}
903
921
  */
904
922
  logout(redirectUri = this.redirectUri) {
905
- this.cache.delete(HAS_SESSION_CACHE_KEY);
923
+ this.sessionStorageCache.delete(HAS_SESSION_CACHE_KEY);
906
924
  this._maybeClearVarnishCookie();
907
925
  this.emit('logout');
908
926
  this.window.location.href = this.logoutUrl(redirectUri);
package/src/version.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // Automatically generated in 'npm version' by scripts/genversion.js
2
2
 
3
3
  'use strict'
4
- const version = '5.0.0';
4
+ const version = '5.0.1-beta';
5
5
  export default version;