Skip to content

Commit

Permalink
Merge branch 'master' into mrschmidt-closewhenidle
Browse files Browse the repository at this point in the history
  • Loading branch information
schmidt-sebastian authored Nov 3, 2017
2 parents c25a96e + 369b411 commit 46dd4a5
Show file tree
Hide file tree
Showing 7 changed files with 268 additions and 20 deletions.
17 changes: 12 additions & 5 deletions packages/auth/src/authstorage.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,13 +137,16 @@ fireauth.authStorage.Key;
* some mobile browsers. A localStorage change in the foreground window
* will not be detected in the background window via the storage event.
* This was detected in iOS 7.x mobile browsers.
* @param {boolean} webStorageSupported Whether browser web storage is
* supported.
* @constructor @struct @final
*/
fireauth.authStorage.Manager = function(
namespace,
separator,
safariLocalStorageNotSynced,
runsInBackground) {
runsInBackground,
webStorageSupported) {
/** @const @private {string} Storage namespace. */
this.namespace_ = namespace;
/** @const @private {string} Storage namespace key separator. */
Expand All @@ -159,6 +162,8 @@ fireauth.authStorage.Manager = function(
* mobile browsers.
*/
this.runsInBackground_ = runsInBackground;
/** @const @private {boolean} Whether browser web storage is supported. */
this.webStorageSupported_ = webStorageSupported;

/**
* @const @private {!Object.<string, !Array<function()>>} The storage event
Expand Down Expand Up @@ -223,7 +228,8 @@ fireauth.authStorage.Manager.getInstance = function() {
fireauth.authStorage.NAMESPACE_,
fireauth.authStorage.SEPARATOR_,
fireauth.util.isSafariLocalStorageNotSynced(),
fireauth.util.runsInBackground());
fireauth.util.runsInBackground(),
fireauth.util.isWebStorageSupported());
}
return fireauth.authStorage.Manager.instance_;
};
Expand Down Expand Up @@ -337,8 +343,7 @@ fireauth.authStorage.Manager.prototype.addListener =
function(dataKey, id, listener) {
var key = this.getKeyName_(dataKey, id);
// Initialize local map for current key if web storage is supported.
if (typeof goog.global['localStorage'] !== 'undefined' &&
typeof goog.global['localStorage']['getItem'] === 'function') {
if (this.webStorageSupported_) {
this.localMap_[key] = goog.global['localStorage']['getItem'](key);
}
if (goog.object.isEmpty(this.listeners_)) {
Expand Down Expand Up @@ -401,7 +406,9 @@ fireauth.authStorage.Manager.prototype.startListeners_ = function() {
if (!this.runsInBackground_ &&
// Add an exception for IE11 and Edge browsers, we should stick to
// indexedDB in that case.
!fireauth.util.isLocalStorageNotSynchronized()) {
!fireauth.util.isLocalStorageNotSynchronized() &&
// Confirm browser web storage is supported as polling relies on it.
this.webStorageSupported_) {
this.startManualListeners_();
}
};
Expand Down
34 changes: 34 additions & 0 deletions packages/auth/src/rpchandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -1499,6 +1499,11 @@ fireauth.RpcHandler.validateVerifyAssertionForExistingResponse_ =
fireauth.RpcHandler.ServerError.USER_NOT_FOUND) {
// This corresponds to user-not-found.
throw new fireauth.AuthError(fireauth.authenum.Error.USER_DELETED);
} else if (response[fireauth.RpcHandler.AuthServerField.ERROR_MESSAGE]) {
// Construct developer facing error message from server code in errorMessage
// field.
throw fireauth.RpcHandler.getDeveloperErrorFromCode_(
response[fireauth.RpcHandler.AuthServerField.ERROR_MESSAGE]);
}
// Need confirmation should not be returned when do not create new user flag
// is set.
Expand Down Expand Up @@ -1543,6 +1548,11 @@ fireauth.RpcHandler.validateVerifyAssertionResponse_ = function(response) {
// owner of the account and then link to the returned credential here.
response['code'] = fireauth.authenum.Error.EMAIL_EXISTS;
error = fireauth.AuthErrorWithCredential.fromPlainObject(response);
} else if (response[fireauth.RpcHandler.AuthServerField.ERROR_MESSAGE]) {
// Construct developer facing error message from server code in errorMessage
// field.
error = fireauth.RpcHandler.getDeveloperErrorFromCode_(
response[fireauth.RpcHandler.AuthServerField.ERROR_MESSAGE]);
}
// If error found, throw it.
if (error) {
Expand Down Expand Up @@ -1979,6 +1989,30 @@ fireauth.RpcHandler.hasError_ = function(resp) {
};


/**
* Returns the developer facing error corresponding to the server code provided.
* @param {string} serverErrorCode The server error message.
* @return {!fireauth.AuthError} The corresponding error object.
* @private
*/
fireauth.RpcHandler.getDeveloperErrorFromCode_ = function(serverErrorCode) {
// Encapsulate the server error code in a typical server error response with
// the code populated within. This will convert the response to a developer
// facing one.
return fireauth.RpcHandler.getDeveloperError_({
'error': {
'errors': [
{
'message': serverErrorCode
}
],
'code': 400,
'message': serverErrorCode
}
});
};


/**
* Converts a server response with errors to a developer-facing AuthError.
* @param {!Object} response The server response.
Expand Down
6 changes: 5 additions & 1 deletion packages/auth/src/storage/indexeddb.js
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,11 @@ fireauth.storage.IndexedDB.prototype.initializeDbAndRun_ =
* @return {boolean} Whether indexedDB is available or not.
*/
fireauth.storage.IndexedDB.isAvailable = function() {
return !!window.indexedDB;
try {
return !!goog.global['indexedDB'];
} catch (e) {
return false;
}
};


Expand Down
15 changes: 14 additions & 1 deletion packages/auth/src/storage/localstorage.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,20 @@ fireauth.storage.LocalStorage = function() {

/** @return {?Storage|undefined} The global localStorage instance. */
fireauth.storage.LocalStorage.getGlobalStorage = function() {
return goog.global['localStorage'];
try {
var storage = goog.global['localStorage'];
// Try editing web storage. If an error is thrown, it may be disabled.
var key = fireauth.util.generateEventId();
if (storage) {
storage['setItem'](key, '1');
storage['removeItem'](key);
}
return storage;
} catch (e) {
// In some cases, browsers with web storage disabled throw an error simply
// on access.
return null;
}
};


Expand Down
15 changes: 14 additions & 1 deletion packages/auth/src/storage/sessionstorage.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,20 @@ fireauth.storage.SessionStorage = function() {

/** @return {?Storage|undefined} The global sessionStorage instance. */
fireauth.storage.SessionStorage.getGlobalStorage = function() {
return goog.global['sessionStorage'];
try {
var storage = goog.global['sessionStorage'];
// Try editing web storage. If an error is thrown, it may be disabled.
var key = fireauth.util.generateEventId();
if (storage) {
storage['setItem'](key, '1');
storage['removeItem'](key);
}
return storage;
} catch (e) {
// In some cases, browsers with web storage disabled throw an error simply
// on access.
return null;
}
};


Expand Down
72 changes: 60 additions & 12 deletions packages/auth/test/authstorage_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ function tearDown() {
* synchronized manager instance used for testing.
*/
function getDefaultManagerInstance() {
return new fireauth.authStorage.Manager('firebase', ':', false, true);
return new fireauth.authStorage.Manager('firebase', ':', false, true, true);
}


Expand Down Expand Up @@ -404,7 +404,8 @@ function testGetSet_persistentStorage_noId() {


function testAddRemoveListeners_localStorage() {
var manager = new fireauth.authStorage.Manager('name', ':', false, true);
var manager =
new fireauth.authStorage.Manager('name', ':', false, true, true);
var listener1 = goog.testing.recordFunction();
var listener2 = goog.testing.recordFunction();
var listener3 = goog.testing.recordFunction();
Expand Down Expand Up @@ -470,7 +471,8 @@ function testAddRemoveListeners_localStorage() {


function testAddRemoveListeners_localStorage_nullKey() {
var manager = new fireauth.authStorage.Manager('name', ':', false, true);
var manager =
new fireauth.authStorage.Manager('name', ':', false, true, true);
var listener1 = goog.testing.recordFunction();
var listener2 = goog.testing.recordFunction();
var listener3 = goog.testing.recordFunction();
Expand Down Expand Up @@ -522,7 +524,8 @@ function testAddRemoveListeners_localStorage_ie10() {
function() {
return true;
});
var manager = new fireauth.authStorage.Manager('name', ':', false, true);
var manager =
new fireauth.authStorage.Manager('name', ':', false, true, true);
var listener1 = goog.testing.recordFunction();
var listener2 = goog.testing.recordFunction();
var listener3 = goog.testing.recordFunction();
Expand Down Expand Up @@ -637,7 +640,8 @@ function testAddRemoveListeners_indexeddb() {
function() {
return mockIndexeddb;
});
var manager = new fireauth.authStorage.Manager('name', ':', false, true);
var manager =
new fireauth.authStorage.Manager('name', ':', false, true, true);
var listener1 = goog.testing.recordFunction();
var listener2 = goog.testing.recordFunction();
var listener3 = goog.testing.recordFunction();
Expand Down Expand Up @@ -714,7 +718,8 @@ function testAddRemoveListeners_indexeddb_cannotRunInBackground() {
return mockIndexeddb;
});
// Cannot run in the background.
var manager = new fireauth.authStorage.Manager('name', ':', false, false);
var manager =
new fireauth.authStorage.Manager('name', ':', false, false, true);
var listener1 = goog.testing.recordFunction();
var listener2 = goog.testing.recordFunction();
var listener3 = goog.testing.recordFunction();
Expand Down Expand Up @@ -757,7 +762,7 @@ function testAddRemoveListeners_indexeddb_cannotRunInBackground() {

function testSafariLocalStorageSync_newEvent() {
var manager =
new fireauth.authStorage.Manager('firebase', ':', true, true);
new fireauth.authStorage.Manager('firebase', ':', true, true, true);
// Simulate Safari bug.
stubs.replace(
fireauth.util,
Expand Down Expand Up @@ -797,7 +802,7 @@ function testSafariLocalStorageSync_cannotRunInBackground() {
// Realistically only storage event should trigger here.
// Test when new data is added to storage.
var manager =
new fireauth.authStorage.Manager('firebase', ':', true, false);
new fireauth.authStorage.Manager('firebase', ':', true, false, true);
// Simulate Safari bug.
stubs.replace(
fireauth.util,
Expand Down Expand Up @@ -837,7 +842,7 @@ function testSafariLocalStorageSync_deletedEvent() {
// Realistically only storage event should trigger here.
// Test when old data is deleted from storage.
var manager =
new fireauth.authStorage.Manager('firebase', ':', true, true);
new fireauth.authStorage.Manager('firebase', ':', true, true, true);
var key1 = {'name': 'authEvent', 'persistent': true};
// Simulate Safari bug.
stubs.replace(
Expand Down Expand Up @@ -879,7 +884,7 @@ function testRunsInBackground_storageEventMode() {
var key = {name: 'authEvent', persistent: 'local'};
var storageKey = 'firebase:authEvent:appId1';
var manager = new fireauth.authStorage.Manager(
'firebase', ':', false, false);
'firebase', ':', false, false, true);
var listener1 = goog.testing.recordFunction();
var expectedEvent = {
type: 'signInViaPopup',
Expand Down Expand Up @@ -923,6 +928,49 @@ function testRunsInBackground_storageEventMode() {
}


function testRunsInBackground_webStorageNotSupported() {
// Test when browser does not run in the background and web storage is not
// supported. Polling should not be turned on.
var key = {name: 'authEvent', persistent: 'local'};
var storageKey = 'firebase:authEvent:appId1';
// Simulate manager doesn't support web storage and can't run in the
// background. Normally when a browser can't run in the background, polling is
// enabled.
var manager = new fireauth.authStorage.Manager(
'firebase', ':', false, false, false);
var listener1 = goog.testing.recordFunction();
var expectedEvent = {
type: 'signInViaPopup',
eventId: '1234',
callbackUrl: 'http://www.example.com/#oauthResponse',
sessionId: 'SESSION_ID'
};

// Add listener.
manager.addListener(key, appId, listener1);
// Test that polling function is not set by updating localStorage with some
// data. This should not happen realistically when web storage is disabled.
window.localStorage.setItem(storageKey, JSON.stringify(expectedEvent));
// Run clock.
clock.tick(1000);
// Listener should not trigger.
assertEquals(0, listener1.getCallCount());
// Clear storage.
window.localStorage.clear();
// Run clock.
clock.tick(1000);
// Listener should not trigger.
assertEquals(0, listener1.getCallCount());
// Save Auth event and confirm listener not triggered.
// This normally simulates polling.
window.localStorage.setItem(storageKey, JSON.stringify(expectedEvent));
// Run clock.
clock.tick(1000);
// Listener should not trigger.
assertEquals(0, listener1.getCallCount());
}


function testRunsInBackground_pollingMode() {
// Test when browser does not run in the background while another tab is in
// foreground.
Expand All @@ -932,7 +980,7 @@ function testRunsInBackground_pollingMode() {
var key = {name: 'authEvent', persistent: 'local'};
var storageKey = 'firebase:authEvent:appId1';
var manager = new fireauth.authStorage.Manager(
'firebase', ':', false, false);
'firebase', ':', false, false, true);
var listener1 = goog.testing.recordFunction();
var expectedEvent = {
type: 'signInViaPopup',
Expand Down Expand Up @@ -984,7 +1032,7 @@ function testRunsInBackground_currentTabChangesIgnored() {
var key = {name: 'authEvent', persistent: 'local'};
var storageKey = 'firebase:authEvent:appId1';
var manager = new fireauth.authStorage.Manager(
'firebase', ':', false, false);
'firebase', ':', false, false, true);
var listener1 = goog.testing.recordFunction();
var expectedEvent = {
type: 'signInViaPopup',
Expand Down
Loading

0 comments on commit 46dd4a5

Please sign in to comment.