Skip to content

Commit 2e0e81b

Browse files
authored
Namespace local storage keys with api key (#132)
* Namespace local storage keys with api key Some weird sites(eg optimizely's visual editor) pollute local storage by acting as a proxy to other sites. These changes should prevent different domains from contaminating each other.
1 parent 5985deb commit 2e0e81b

File tree

9 files changed

+239
-112
lines changed

9 files changed

+239
-112
lines changed

amplitude.js

Lines changed: 69 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5673,7 +5673,7 @@ var DEFAULT_OPTIONS = {
56735673
*/
56745674
var AmplitudeClient = function AmplitudeClient(instanceName) {
56755675
this._instanceName = utils.isEmptyString(instanceName) ? constants.DEFAULT_INSTANCE : instanceName.toLowerCase();
5676-
this._storageSuffix = this._instanceName === constants.DEFAULT_INSTANCE ? '' : '_' + this._instanceName;
5676+
this._legacyStorageSuffix = this._instanceName === constants.DEFAULT_INSTANCE ? '' : '_' + this._instanceName;
56775677
this._unsentEvents = [];
56785678
this._unsentIdentifys = [];
56795679
this._ua = new uaParser(navigator.userAgent).getResult();
@@ -5716,6 +5716,7 @@ AmplitudeClient.prototype.init = function init(apiKey, opt_userId, opt_config, o
57165716

57175717
try {
57185718
this.options.apiKey = apiKey;
5719+
this._storageSuffix = '_' + apiKey + this._legacyStorageSuffix;
57195720
_parseConfig(this.options, opt_config);
57205721
this.cookieStorage.options({
57215722
expirationDays: this.options.cookieExpiration,
@@ -5864,6 +5865,29 @@ AmplitudeClient.prototype._apiKeySet = function _apiKeySet(methodName) {
58645865
*/
58655866
AmplitudeClient.prototype._loadSavedUnsentEvents = function _loadSavedUnsentEvents(unsentKey) {
58665867
var savedUnsentEventsString = this._getFromStorage(localStorage$1, unsentKey);
5868+
var events = this._parseSavedUnsentEventsString(savedUnsentEventsString, unsentKey);
5869+
5870+
var savedUnsentEventsStringLegacy = this._getFromStorageLegacy(localStorage$1, unsentKey);
5871+
var legacyEvents = this._parseSavedUnsentEventsString(savedUnsentEventsStringLegacy, unsentKey);
5872+
5873+
var unsentEvents = legacyEvents.concat(events);
5874+
5875+
// Migrate legacy events out of storage
5876+
this._removeFromLegacyStorage(localStorage$1, unsentKey);
5877+
this._setInStorage(localStorage$1, unsentKey, JSON.stringify(unsentEvents));
5878+
5879+
return unsentEvents;
5880+
};
5881+
5882+
AmplitudeClient.prototype._removeFromLegacyStorage = function _removeFromLegacyStorage(storage, key) {
5883+
storage.removeItem(key + this._legacyStorageSuffix);
5884+
};
5885+
5886+
/**
5887+
* Load saved events from localStorage. JSON deserializes event array. Handles case where string is corrupted.
5888+
* @private
5889+
*/
5890+
AmplitudeClient.prototype._parseSavedUnsentEventsString = function _parseSavedUnsentEventsString(savedUnsentEventsString, unsentKey) {
58675891
if (utils.isEmptyString(savedUnsentEventsString)) {
58685892
return []; // new app, does not have any saved events
58695893
}
@@ -5977,6 +6001,15 @@ AmplitudeClient.prototype._getFromStorage = function _getFromStorage(storage, ke
59776001
return storage.getItem(key + this._storageSuffix);
59786002
};
59796003

6004+
/**
6005+
* Helper function to fetch values from storage
6006+
* Storage argument allows for localStoraoge and sessionStoraoge
6007+
* @private
6008+
*/
6009+
AmplitudeClient.prototype._getFromStorageLegacy = function _getFromStorageLegacy(storage, key) {
6010+
return storage.getItem(key + this._legacyStorageSuffix);
6011+
};
6012+
59806013
/**
59816014
* Helper function to set values in storage
59826015
* Storage argument allows for localStoraoge and sessionStoraoge
@@ -5994,7 +6027,7 @@ AmplitudeClient.prototype._setInStorage = function _setInStorage(storage, key, v
59946027
*/
59956028
var _upgradeCookeData = function _upgradeCookeData(scope) {
59966029
// skip if migration already happened
5997-
var cookieData = scope.cookieStorage.get(scope.options.cookieName);
6030+
var cookieData = scope.cookieStorage.get(scope.options.cookieName + scope._storageSuffix);
59986031
if (type(cookieData) === 'object' && cookieData.deviceId && cookieData.sessionId && cookieData.lastEventTime) {
59996032
return;
60006033
}
@@ -6047,34 +6080,45 @@ var _upgradeCookeData = function _upgradeCookeData(scope) {
60476080
*/
60486081
var _loadCookieData = function _loadCookieData(scope) {
60496082
var cookieData = scope.cookieStorage.get(scope.options.cookieName + scope._storageSuffix);
6083+
60506084
if (type(cookieData) === 'object') {
6051-
if (cookieData.deviceId) {
6052-
scope.options.deviceId = cookieData.deviceId;
6053-
}
6054-
if (cookieData.userId) {
6055-
scope.options.userId = cookieData.userId;
6056-
}
6057-
if (cookieData.optOut !== null && cookieData.optOut !== undefined) {
6058-
scope.options.optOut = cookieData.optOut;
6059-
}
6060-
if (cookieData.sessionId) {
6061-
scope._sessionId = parseInt(cookieData.sessionId);
6062-
}
6063-
if (cookieData.lastEventTime) {
6064-
scope._lastEventTime = parseInt(cookieData.lastEventTime);
6065-
}
6066-
if (cookieData.eventId) {
6067-
scope._eventId = parseInt(cookieData.eventId);
6068-
}
6069-
if (cookieData.identifyId) {
6070-
scope._identifyId = parseInt(cookieData.identifyId);
6071-
}
6072-
if (cookieData.sequenceNumber) {
6073-
scope._sequenceNumber = parseInt(cookieData.sequenceNumber);
6085+
_loadCookieDataProps(scope, cookieData);
6086+
} else {
6087+
var legacyCookieData = scope.cookieStorage.get(scope.options.cookieName + scope._legacyStorageSuffix);
6088+
if (type(legacyCookieData) === 'object') {
6089+
scope.cookieStorage.remove(scope.options.cookieName + scope._legacyStorageSuffix);
6090+
_loadCookieDataProps(scope, legacyCookieData);
60746091
}
60756092
}
60766093
};
60776094

6095+
var _loadCookieDataProps = function _loadCookieDataProps(scope, cookieData) {
6096+
if (cookieData.deviceId) {
6097+
scope.options.deviceId = cookieData.deviceId;
6098+
}
6099+
if (cookieData.userId) {
6100+
scope.options.userId = cookieData.userId;
6101+
}
6102+
if (cookieData.optOut !== null && cookieData.optOut !== undefined) {
6103+
scope.options.optOut = cookieData.optOut;
6104+
}
6105+
if (cookieData.sessionId) {
6106+
scope._sessionId = parseInt(cookieData.sessionId);
6107+
}
6108+
if (cookieData.lastEventTime) {
6109+
scope._lastEventTime = parseInt(cookieData.lastEventTime);
6110+
}
6111+
if (cookieData.eventId) {
6112+
scope._eventId = parseInt(cookieData.eventId);
6113+
}
6114+
if (cookieData.identifyId) {
6115+
scope._identifyId = parseInt(cookieData.identifyId);
6116+
}
6117+
if (cookieData.sequenceNumber) {
6118+
scope._sequenceNumber = parseInt(cookieData.sequenceNumber);
6119+
}
6120+
};
6121+
60786122
/**
60796123
* Saves deviceId, userId, event meta data to amplitude cookie
60806124
* @private

amplitude.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

karma.conf.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ module.exports = function(config) {
3333
sauceLabs: {
3434
testName: 'Amplitude JavaScript SDK',
3535
},
36+
preprocessors: {
37+
'**/*.js': ['sourcemap']
38+
},
3639
frameworks: ['mocha', 'chai', 'sinon'],
3740
files: ['amplitude-snippet.min.js', 'build/snippet-tests.js', 'build/tests.js'],
3841
reporters: ['mocha', 'saucelabs'],

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"karma-mocha": "^1.3.0",
3434
"karma-mocha-reporter": "^2.2.5",
3535
"karma-sauce-launcher": "^1.2.0",
36+
"karma-sourcemap-loader": "^0.3.7",
3637
"karma-sinon": "^1.0.5",
3738
"mocha": "^4.0.1",
3839
"rollup": "^0.49.3",

rollup.test.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@ import config from './rollup.config.js';
22

33
config.input = 'test/tests.js';
44
config.output.file = 'build/tests.js';
5+
config.output.sourcemap = true;
56

67
export default config;

src/amplitude-client.js

Lines changed: 70 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import DEFAULT_OPTIONS from './options';
2323
*/
2424
var AmplitudeClient = function AmplitudeClient(instanceName) {
2525
this._instanceName = utils.isEmptyString(instanceName) ? Constants.DEFAULT_INSTANCE : instanceName.toLowerCase();
26-
this._storageSuffix = this._instanceName === Constants.DEFAULT_INSTANCE ? '' : '_' + this._instanceName;
26+
this._legacyStorageSuffix = this._instanceName === Constants.DEFAULT_INSTANCE ? '' : '_' + this._instanceName;
2727
this._unsentEvents = [];
2828
this._unsentIdentifys = [];
2929
this._ua = new UAParser(navigator.userAgent).getResult();
@@ -66,6 +66,7 @@ AmplitudeClient.prototype.init = function init(apiKey, opt_userId, opt_config, o
6666

6767
try {
6868
this.options.apiKey = apiKey;
69+
this._storageSuffix = '_' + apiKey + this._legacyStorageSuffix;
6970
_parseConfig(this.options, opt_config);
7071
this.cookieStorage.options({
7172
expirationDays: this.options.cookieExpiration,
@@ -221,6 +222,30 @@ AmplitudeClient.prototype._apiKeySet = function _apiKeySet(methodName) {
221222
*/
222223
AmplitudeClient.prototype._loadSavedUnsentEvents = function _loadSavedUnsentEvents(unsentKey) {
223224
var savedUnsentEventsString = this._getFromStorage(localStorage, unsentKey);
225+
var events = this._parseSavedUnsentEventsString(savedUnsentEventsString, unsentKey);
226+
227+
var savedUnsentEventsStringLegacy = this._getFromStorageLegacy(localStorage, unsentKey);
228+
var legacyEvents = this._parseSavedUnsentEventsString(savedUnsentEventsStringLegacy, unsentKey);
229+
230+
var unsentEvents = legacyEvents.concat(events);
231+
232+
// Migrate legacy events out of storage
233+
this._removeFromLegacyStorage(localStorage, unsentKey);
234+
this._setInStorage(localStorage, unsentKey, JSON.stringify(unsentEvents));
235+
236+
return unsentEvents;
237+
};
238+
239+
240+
AmplitudeClient.prototype._removeFromLegacyStorage = function _removeFromLegacyStorage(storage, key) {
241+
storage.removeItem(key + this._legacyStorageSuffix);
242+
};
243+
244+
/**
245+
* Load saved events from localStorage. JSON deserializes event array. Handles case where string is corrupted.
246+
* @private
247+
*/
248+
AmplitudeClient.prototype._parseSavedUnsentEventsString = function _parseSavedUnsentEventsString(savedUnsentEventsString, unsentKey) {
224249
if (utils.isEmptyString(savedUnsentEventsString)) {
225250
return []; // new app, does not have any saved events
226251
}
@@ -333,6 +358,15 @@ AmplitudeClient.prototype._getFromStorage = function _getFromStorage(storage, ke
333358
return storage.getItem(key + this._storageSuffix);
334359
};
335360

361+
/**
362+
* Helper function to fetch values from storage
363+
* Storage argument allows for localStoraoge and sessionStoraoge
364+
* @private
365+
*/
366+
AmplitudeClient.prototype._getFromStorageLegacy = function _getFromStorageLegacy(storage, key) {
367+
return storage.getItem(key + this._legacyStorageSuffix);
368+
};
369+
336370
/**
337371
* Helper function to set values in storage
338372
* Storage argument allows for localStoraoge and sessionStoraoge
@@ -350,7 +384,7 @@ AmplitudeClient.prototype._setInStorage = function _setInStorage(storage, key, v
350384
*/
351385
var _upgradeCookeData = function _upgradeCookeData(scope) {
352386
// skip if migration already happened
353-
var cookieData = scope.cookieStorage.get(scope.options.cookieName);
387+
var cookieData = scope.cookieStorage.get(scope.options.cookieName + scope._storageSuffix);
354388
if (type(cookieData) === 'object' && cookieData.deviceId && cookieData.sessionId && cookieData.lastEventTime) {
355389
return;
356390
}
@@ -403,34 +437,45 @@ var _upgradeCookeData = function _upgradeCookeData(scope) {
403437
*/
404438
var _loadCookieData = function _loadCookieData(scope) {
405439
var cookieData = scope.cookieStorage.get(scope.options.cookieName + scope._storageSuffix);
440+
406441
if (type(cookieData) === 'object') {
407-
if (cookieData.deviceId) {
408-
scope.options.deviceId = cookieData.deviceId;
409-
}
410-
if (cookieData.userId) {
411-
scope.options.userId = cookieData.userId;
412-
}
413-
if (cookieData.optOut !== null && cookieData.optOut !== undefined) {
414-
scope.options.optOut = cookieData.optOut;
415-
}
416-
if (cookieData.sessionId) {
417-
scope._sessionId = parseInt(cookieData.sessionId);
418-
}
419-
if (cookieData.lastEventTime) {
420-
scope._lastEventTime = parseInt(cookieData.lastEventTime);
421-
}
422-
if (cookieData.eventId) {
423-
scope._eventId = parseInt(cookieData.eventId);
424-
}
425-
if (cookieData.identifyId) {
426-
scope._identifyId = parseInt(cookieData.identifyId);
427-
}
428-
if (cookieData.sequenceNumber) {
429-
scope._sequenceNumber = parseInt(cookieData.sequenceNumber);
442+
_loadCookieDataProps(scope, cookieData);
443+
} else {
444+
var legacyCookieData = scope.cookieStorage.get(scope.options.cookieName + scope._legacyStorageSuffix);
445+
if (type(legacyCookieData) === 'object') {
446+
scope.cookieStorage.remove(scope.options.cookieName + scope._legacyStorageSuffix);
447+
_loadCookieDataProps(scope, legacyCookieData);
430448
}
431449
}
432450
};
433451

452+
var _loadCookieDataProps = function _loadCookieDataProps(scope, cookieData) {
453+
if (cookieData.deviceId) {
454+
scope.options.deviceId = cookieData.deviceId;
455+
}
456+
if (cookieData.userId) {
457+
scope.options.userId = cookieData.userId;
458+
}
459+
if (cookieData.optOut !== null && cookieData.optOut !== undefined) {
460+
scope.options.optOut = cookieData.optOut;
461+
}
462+
if (cookieData.sessionId) {
463+
scope._sessionId = parseInt(cookieData.sessionId);
464+
}
465+
if (cookieData.lastEventTime) {
466+
scope._lastEventTime = parseInt(cookieData.lastEventTime);
467+
}
468+
if (cookieData.eventId) {
469+
scope._eventId = parseInt(cookieData.eventId);
470+
}
471+
if (cookieData.identifyId) {
472+
scope._identifyId = parseInt(cookieData.identifyId);
473+
}
474+
if (cookieData.sequenceNumber) {
475+
scope._sequenceNumber = parseInt(cookieData.sequenceNumber);
476+
}
477+
};
478+
434479
/**
435480
* Saves deviceId, userId, event meta data to amplitude cookie
436481
* @private

0 commit comments

Comments
 (0)