Skip to content

Commit 27d2778

Browse files
bojeil-googlewti806
authored andcommitted
Clears getRedirectResult() in AuthEventManager for a specified Auth instance when it is deleted. (#973)
app.auth().getRedirectResult() => resolves with redirect result provided previous page called a redirect operation. app.delete(); // deletes auth instance corresponding to app and clears redirect result. // re-initialize app with same parameters. app = firebase.initializeApp(config); app.auth().getRedirectResult() should resolve with null. This is needed for firebaseui-web react wrapper which on component unmount will delete the firebaseui instance (which deletes the internal auth instance). When the component is mounted again, the previously temp auth instance was still caching the redirect result. firebase/firebaseui-web#440 Cleans up private property access in AuthEventManager tests.
1 parent 7210db8 commit 27d2778

File tree

4 files changed

+130
-33
lines changed

4 files changed

+130
-33
lines changed

packages/auth/src/auth.js

+1
Original file line numberDiff line numberDiff line change
@@ -1830,6 +1830,7 @@ fireauth.Auth.prototype.delete = function() {
18301830
// Unsubscribe from Auth event handling.
18311831
if (this.authEventManager_) {
18321832
this.authEventManager_.unsubscribe(this);
1833+
this.authEventManager_.clearRedirectResult();
18331834
}
18341835
return firebase.Promise.resolve();
18351836
};

packages/auth/src/autheventmanager.js

+51-4
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ fireauth.AuthEventManager = function(authDomain, apiKey, appName) {
6363
* @private {boolean} Whether the Auth event manager instance is initialized.
6464
*/
6565
this.initialized_ = false;
66-
/** @private {!function(?fireauth.AuthEvent)} The Auth event handler. */
66+
/** @private {function(?fireauth.AuthEvent)} The Auth event handler. */
6767
this.authEventHandler_ = goog.bind(this.handleAuthEvent_, this);
6868
/** @private {!fireauth.RedirectAuthEventProcessor} The redirect event
6969
* processor. */
@@ -110,6 +110,20 @@ fireauth.AuthEventManager = function(authDomain, apiKey, appName) {
110110
};
111111

112112

113+
/**
114+
* @return {!fireauth.RedirectAuthEventProcessor} The redirect event processor.
115+
*/
116+
fireauth.AuthEventManager.prototype.getRedirectAuthEventProcessor = function() {
117+
return this.redirectAuthEventProcessor_;
118+
};
119+
120+
121+
/** @return {!fireauth.PopupAuthEventProcessor} The popup event processor. */
122+
fireauth.AuthEventManager.prototype.getPopupAuthEventProcessor = function() {
123+
return this.popupAuthEventProcessor_;
124+
};
125+
126+
113127
/**
114128
* Instantiates an OAuth sign-in handler depending on the current environment
115129
* and returns it.
@@ -155,6 +169,15 @@ fireauth.AuthEventManager.prototype.reset = function() {
155169
};
156170

157171

172+
/**
173+
* Clears the cached redirect result as long as there is no pending redirect
174+
* result being processed. Unrecoverable errors will not be cleared.
175+
*/
176+
fireauth.AuthEventManager.prototype.clearRedirectResult = function() {
177+
this.redirectAuthEventProcessor_.clearRedirectResult();
178+
};
179+
180+
158181
/**
159182
* @typedef {{
160183
* user: (?fireauth.AuthUser|undefined),
@@ -539,7 +562,7 @@ fireauth.AuthEventManager.prototype.startPopupTimeout =
539562

540563

541564
/**
542-
* @private {!Object.<!string, !fireauth.AuthEventManager>} Map containing
565+
* @private {!Object.<string, !fireauth.AuthEventManager>} Map containing
543566
* Firebase event manager instances keyed by Auth event manager ID.
544567
*/
545568
fireauth.AuthEventManager.manager_ = {};
@@ -634,9 +657,16 @@ fireauth.RedirectAuthEventProcessor = function(manager) {
634657
this.redirectReject_ = [];
635658
/** @private {?goog.Promise} Pending timeout promise for redirect. */
636659
this.redirectTimeoutPromise_ = null;
637-
/** @private {boolean} Whether redirect result is resolved. This is true
638-
when a valid Auth event has been triggered. */
660+
/**
661+
* @private {boolean} Whether redirect result is resolved. This is true
662+
* when a valid Auth event has been triggered.
663+
*/
639664
this.redirectResultResolved_ = false;
665+
/**
666+
* @private {boolean} Whether an unrecoverable error was detected. This
667+
* includes web storage unsupported or operation not allowed errors.
668+
*/
669+
this.unrecoverableErrorDetected_ = false;
640670
};
641671

642672

@@ -684,6 +714,8 @@ fireauth.RedirectAuthEventProcessor.prototype.processAuthEvent =
684714
authEvent.getError() &&
685715
authEvent.getError()['code'] == 'auth/operation-not-supported-in-this-' +
686716
'environment';
717+
this.unrecoverableErrorDetected_ =
718+
!!(isWebStorageNotSupported || isOperationNotSupported);
687719
// UNKNOWN mode is always triggered on load by iframe when no popup/redirect
688720
// data is available. If web storage unsupported error is thrown, process as
689721
// error and not as unknown event. If the operation is not supported in this
@@ -722,6 +754,19 @@ fireauth.RedirectAuthEventProcessor.prototype.defaultToEmptyResponse =
722754
};
723755

724756

757+
/**
758+
* Clears the cached redirect result as long as there is no pending redirect
759+
* result being processed. Unrecoverable errors will not be cleared.
760+
*/
761+
fireauth.RedirectAuthEventProcessor.prototype.clearRedirectResult = function() {
762+
// Clear the result if it is already resolved and no unrecoverable errors are
763+
// detected.
764+
if (this.redirectResultResolved_ && !this.unrecoverableErrorDetected_) {
765+
this.setRedirectResult_(false, null, null);
766+
}
767+
};
768+
769+
725770
/**
726771
* Processes the unknown event.
727772
* @return {!goog.Promise<undefined>}
@@ -921,6 +966,8 @@ fireauth.RedirectAuthEventProcessor.prototype.startRedirectTimeout_ =
921966
.then(function() {
922967
// If not resolved yet, reject with timeout error.
923968
if (!self.redirectedUserPromise_) {
969+
// Consider redirect result resolved.
970+
self.redirectResultResolved_ = true;
924971
self.setRedirectResult_(true, null, error);
925972
}
926973
});

packages/auth/test/auth_test.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -1988,7 +1988,8 @@ function testAuth_authEventManager() {
19881988
initializeMockStorage();
19891989
var expectedManager = {
19901990
'subscribe': goog.testing.recordFunction(),
1991-
'unsubscribe': goog.testing.recordFunction()
1991+
'unsubscribe': goog.testing.recordFunction(),
1992+
'clearRedirectResult': goog.testing.recordFunction()
19921993
};
19931994
// Return stub manager.
19941995
stubs.replace(
@@ -2012,10 +2013,14 @@ function testAuth_authEventManager() {
20122013
assertEquals(1, expectedManager.subscribe.getCallCount());
20132014
assertEquals(
20142015
auth1, expectedManager.subscribe.getLastCall().getArgument(0));
2016+
assertEquals(0, expectedManager.clearRedirectResult.getCallCount());
2017+
// Delete should trigger unsubscribe and redirect result clearing.
20152018
auth1.delete();
20162019
// After destroy, Auth should be unsubscribed.
20172020
assertEquals(1, expectedManager.subscribe.getCallCount());
20182021
assertEquals(1, expectedManager.unsubscribe.getCallCount());
2022+
// Redirect result should also be cleared.
2023+
assertEquals(1, expectedManager.clearRedirectResult.getCallCount());
20192024
assertEquals(
20202025
auth1, expectedManager.unsubscribe.getLastCall().getArgument(0));
20212026
asyncTestCase.signal();

packages/auth/test/autheventmanager_test.js

+72-28
Original file line numberDiff line numberDiff line change
@@ -1611,7 +1611,12 @@ function testAuthEventManager_nonCordovaIosOrAndroidFileEnvironment() {
16111611
// All popup/redirect methods should fail with operation not supported errors.
16121612
manager.getRedirectResult().thenCatch(function(error) {
16131613
assertErrorEquals(expectedError, error);
1614-
asyncTestCase.signal();
1614+
// Clear redirect result will not clear an operation not supported error.
1615+
manager.clearRedirectResult();
1616+
manager.getRedirectResult().thenCatch(function(error) {
1617+
assertErrorEquals(expectedError, error);
1618+
asyncTestCase.signal();
1619+
});
16151620
});
16161621
manager.processRedirect('linkViaRedirect', provider, '1234')
16171622
.thenCatch(function(error) {
@@ -1781,7 +1786,12 @@ function testProcessRedirect_error_cordovahandler() {
17811786
assertFalse(status);
17821787
manager.getRedirectResult().thenCatch(function(error) {
17831788
assertErrorEquals(expectedError, error);
1784-
asyncTestCase.signal();
1789+
// Clear redirect result should clear recoverable errors.
1790+
manager.clearRedirectResult();
1791+
manager.getRedirectResult().then(function(result) {
1792+
assertNull(result.user);
1793+
asyncTestCase.signal();
1794+
});
17851795
});
17861796
});
17871797
}
@@ -1902,7 +1912,12 @@ function testGetRedirectResult_success_cordovahandler() {
19021912
// Initial expected result should resolve.
19031913
manager.getRedirectResult().then(function(result) {
19041914
assertEquals(expectedResult, result);
1905-
asyncTestCase.signal();
1915+
// Clear redirect result should clear successful results.
1916+
manager.clearRedirectResult();
1917+
manager.getRedirectResult().then(function(result) {
1918+
assertNull(result.user);
1919+
asyncTestCase.signal();
1920+
});
19061921
});
19071922
});
19081923
}
@@ -1946,7 +1961,12 @@ function testGetRedirectResult_error_cordovahandler() {
19461961
// Initial expected result should resolve.
19471962
manager.getRedirectResult().thenCatch(function(error) {
19481963
assertErrorEquals(expectedError, error);
1949-
asyncTestCase.signal();
1964+
// Clear redirect result should clear recoverable errors.
1965+
manager.clearRedirectResult();
1966+
manager.getRedirectResult().then(function(result) {
1967+
assertNull(result.user);
1968+
asyncTestCase.signal();
1969+
});
19501970
});
19511971
});
19521972
}
@@ -2177,7 +2197,7 @@ function testProcessAuthEvent_invalidAuthEvent() {
21772197
var expectedAuthEvent = null;
21782198
var manager = fireauth.AuthEventManager.getManager(
21792199
authDomain1, apiKey1, appName1);
2180-
manager.redirectAuthEventProcessor_.processAuthEvent(
2200+
manager.getRedirectAuthEventProcessor().processAuthEvent(
21812201
expectedAuthEvent, handler).thenCatch(function(error) {
21822202
assertErrorEquals(
21832203
new fireauth.AuthError(fireauth.authenum.Error.INVALID_AUTH_EVENT),
@@ -2204,7 +2224,7 @@ function testProcessAuthEvent_unknownAuthEvent() {
22042224
assertObjectEquals(expectedResult, result);
22052225
asyncTestCase.signal();
22062226
});
2207-
manager.redirectAuthEventProcessor_.processAuthEvent(
2227+
manager.getRedirectAuthEventProcessor().processAuthEvent(
22082228
expectedAuthEvent, handler).then(function() {
22092229
asyncTestCase.signal();
22102230
});
@@ -2261,7 +2281,13 @@ function testProcessAuthEvent_unknownAuthEvent_webStorageNotSupported() {
22612281
manager.subscribe(handler);
22622282
manager.getRedirectResult().thenCatch(function(error) {
22632283
assertErrorEquals(expectedError, error);
2264-
asyncTestCase.signal();
2284+
// Unrecoverable errors like unsupported web storage should not be
2285+
// cleared.
2286+
manager.clearRedirectResult();
2287+
manager.getRedirectResult().thenCatch(function(error) {
2288+
assertErrorEquals(expectedError, error);
2289+
asyncTestCase.signal();
2290+
});
22652291
});
22662292
});
22672293
}
@@ -2279,7 +2305,7 @@ function testProcessAuthEvent_popupErrorAuthEvent() {
22792305
new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR));
22802306
var manager = fireauth.AuthEventManager.getManager(
22812307
authDomain1, apiKey1, appName1);
2282-
manager.popupAuthEventProcessor_.processAuthEvent(
2308+
manager.getPopupAuthEventProcessor().processAuthEvent(
22832309
expectedAuthEvent, handler).then(function() {
22842310
// Resolve popup with error.
22852311
assertEquals(1, handler.resolvePendingPopupEvent.getCallCount());
@@ -2316,7 +2342,7 @@ function testProcessAuthEvent_redirectErrorAuthEvent() {
23162342
assertErrorEquals(expectedError, error);
23172343
asyncTestCase.signal();
23182344
});
2319-
manager.redirectAuthEventProcessor_.processAuthEvent(
2345+
manager.getRedirectAuthEventProcessor().processAuthEvent(
23202346
expectedAuthEvent, handler).then(function() {
23212347
// Should not be called as event is not a popup type.
23222348
assertEquals(0, handler.resolvePendingPopupEvent.getCallCount());
@@ -2348,7 +2374,7 @@ function testProcessAuthEvent_finisher_successfulPopupAuthEvent() {
23482374
'SESSION_ID');
23492375
var manager = fireauth.AuthEventManager.getManager(
23502376
authDomain1, apiKey1, appName1);
2351-
manager.popupAuthEventProcessor_.processAuthEvent(
2377+
manager.getPopupAuthEventProcessor().processAuthEvent(
23522378
expectedAuthEvent, handler).then(function() {
23532379
// Resolve popup with success.
23542380
assertEquals(1, handler.resolvePendingPopupEvent.getCallCount());
@@ -2389,7 +2415,7 @@ function testProcessAuthEvent_finisher_errorPopupAuthEvent() {
23892415
'SESSION_ID');
23902416
var manager = fireauth.AuthEventManager.getManager(
23912417
authDomain1, apiKey1, appName1);
2392-
manager.popupAuthEventProcessor_.processAuthEvent(
2418+
manager.getPopupAuthEventProcessor().processAuthEvent(
23932419
expectedAuthEvent, handler).then(function() {
23942420
// Resolve popup with error.
23952421
assertEquals(1, handler.resolvePendingPopupEvent.getCallCount());
@@ -2439,7 +2465,7 @@ function testProcessAuthEvent_finisher_successfulRedirectAuthEvent() {
24392465
assertObjectEquals(expectedRedirectResult, result);
24402466
asyncTestCase.signal();
24412467
});
2442-
manager.redirectAuthEventProcessor_.processAuthEvent(
2468+
manager.getRedirectAuthEventProcessor().processAuthEvent(
24432469
expectedAuthEvent, handler).then(function() {
24442470
// Popup resolve should not be called as this is not a popup event.
24452471
assertEquals(0, handler.resolvePendingPopupEvent.getCallCount());
@@ -2472,7 +2498,7 @@ function testProcessAuthEvent_finisher_errorRedirectAuthEvent() {
24722498
assertErrorEquals(expectedError, error);
24732499
asyncTestCase.signal();
24742500
});
2475-
manager.redirectAuthEventProcessor_.processAuthEvent(
2501+
manager.getRedirectAuthEventProcessor().processAuthEvent(
24762502
expectedAuthEvent, handler).then(function() {
24772503
// Popup resolve should not be called as this is not a popup event.
24782504
assertEquals(0, handler.resolvePendingPopupEvent.getCallCount());
@@ -2494,7 +2520,7 @@ function testProcessAuthEvent_noHandler() {
24942520
'SESSION_ID');
24952521
var manager = fireauth.AuthEventManager.getManager(
24962522
authDomain1, apiKey1, appName1);
2497-
manager.redirectAuthEventProcessor_.processAuthEvent(
2523+
manager.getRedirectAuthEventProcessor().processAuthEvent(
24982524
expectedAuthEvent, handler).thenCatch(function(error) {
24992525
assertErrorEquals(expectedError, error);
25002526
asyncTestCase.signal();
@@ -2556,7 +2582,12 @@ function testRedirectResult_timeout() {
25562582
authDomain1, apiKey1, appName1);
25572583
manager.getRedirectResult().thenCatch(function(error) {
25582584
assertErrorEquals(expectedError, error);
2559-
asyncTestCase.signal();
2585+
// Redirect results with recoverable errors should be cleared.
2586+
manager.clearRedirectResult();
2587+
manager.getRedirectResult().then(function(result) {
2588+
assertNull(result.user);
2589+
asyncTestCase.signal();
2590+
});
25602591
});
25612592
// Speed up timeout.
25622593
clock.tick(timeoutDelay);
@@ -2567,7 +2598,9 @@ function testRedirectResult_overwritePreviousRedirectResult() {
25672598
// Once redirect result is determined, it can still be overwritten, though
25682599
// in some cases like ifchandler which gets redirect result from
25692600
// sessionStorage, this should not happen.
2570-
asyncTestCase.waitForSignals(4);
2601+
// Test also that clearRedirectResult will clear the cached result as long as
2602+
// the operation is not pending.
2603+
asyncTestCase.waitForSignals(2);
25712604
handler.getAuthEventHandlerFinisher = function(mode, eventId) {
25722605
return function(requestUri, sessionId) {
25732606
// Iterate through results array each call.
@@ -2595,21 +2628,32 @@ function testRedirectResult_overwritePreviousRedirectResult() {
25952628
assertObjectEquals(expectedRedirectResult, result);
25962629
asyncTestCase.signal();
25972630
});
2598-
manager.redirectAuthEventProcessor_.processAuthEvent(
2631+
// Clear redirect result on a pending operation should have no effect.
2632+
// Above getRedirectResult() should still resolve with expected first result.
2633+
manager.clearRedirectResult();
2634+
manager.getRedirectAuthEventProcessor().processAuthEvent(
25992635
expectedAuthEvent, handler).then(function() {
26002636
// Popup resolve should not be called as this is not a popup event.
26012637
assertEquals(0, handler.resolvePendingPopupEvent.getCallCount());
2602-
asyncTestCase.signal();
2638+
// Clear redirect result after resolved operation should reset result.
2639+
manager.clearRedirectResult();
2640+
return manager.getRedirectResult();
2641+
}).then(function(result) {
2642+
assertNull(result.user);
26032643
// Call Second time.
2604-
manager.redirectAuthEventProcessor_.processAuthEvent(
2605-
expectedAuthEvent, handler).then(function() {
2606-
assertEquals(0, handler.resolvePendingPopupEvent.getCallCount());
2607-
asyncTestCase.signal();
2608-
// getRedirectResult should resolve with the second expected result.
2609-
manager.getRedirectResult().then(function(result) {
2610-
assertObjectEquals(expectedRedirectResult2, result);
2611-
asyncTestCase.signal();
2612-
});
2613-
});
2644+
return manager.getRedirectAuthEventProcessor().processAuthEvent(
2645+
expectedAuthEvent, handler);
2646+
}).then(function() {
2647+
assertEquals(0, handler.resolvePendingPopupEvent.getCallCount());
2648+
// getRedirectResult should resolve with the second expected result.
2649+
return manager.getRedirectResult();
2650+
}).then(function(result) {
2651+
assertObjectEquals(expectedRedirectResult2, result);
2652+
// Clear redirect result after resolved operation should reset result.
2653+
manager.clearRedirectResult();
2654+
return manager.getRedirectResult();
2655+
}).then(function(result) {
2656+
assertNull(result.user);
2657+
asyncTestCase.signal();
26142658
});
26152659
}

0 commit comments

Comments
 (0)