Skip to content

Commit

Permalink
Merge 1a82a1e into 386fd24
Browse files Browse the repository at this point in the history
  • Loading branch information
wti806 committed Aug 2, 2019
2 parents 386fd24 + 1a82a1e commit ec1e9be
Show file tree
Hide file tree
Showing 2 changed files with 255 additions and 2 deletions.
43 changes: 41 additions & 2 deletions packages/auth/src/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,10 @@ fireauth.Auth = function(app) {
this.userDeleteListener_ = goog.bind(this.handleUserDelete_, this);
/** @private {!function(!Object)} The handler for user invalidation. */
this.userInvalidatedListener_ = goog.bind(this.handleUserInvalidated_, this);
/**
* @private {?fireauth.AuthEventManager} The Auth event manager instance.
*/
this.authEventManager_ = null;
// TODO: find better way to enable or disable auth event manager.
if (fireauth.AuthEventManager.ENABLED) {
// Initialize Auth event manager to handle popup and redirect operations.
Expand Down Expand Up @@ -713,8 +717,9 @@ fireauth.Auth.prototype.signInWithRedirect = function(provider) {
* redirect sign, will reject with the proper error. If no redirect operation
* called, resolves with null.
* @return {!goog.Promise<!fireauth.AuthEventManager.Result>}
* @private
*/
fireauth.Auth.prototype.getRedirectResult = function() {
fireauth.Auth.prototype.getRedirectResultWithoutClearing_ = function() {
// Check if popup and redirect are supported in this environment.
if (!fireauth.util.isPopupRedirectSupported()) {
return goog.Promise.reject(new fireauth.AuthError(
Expand All @@ -737,6 +742,29 @@ fireauth.Auth.prototype.getRedirectResult = function() {
};


/**
* In addition to returning the redirect result as in
* `getRedirectResultWithoutClearing_`, this will also clear the cached
* redirect result for security reasons.
* @return {!goog.Promise<!fireauth.AuthEventManager.Result>}
*/
fireauth.Auth.prototype.getRedirectResult = function() {
return this.getRedirectResultWithoutClearing_()
.then((result) => {
if (this.authEventManager_) {
this.authEventManager_.clearRedirectResult();
}
return result;
})
.thenCatch((error) => {
if (this.authEventManager_) {
this.authEventManager_.clearRedirectResult();
}
throw error;
});
};


/**
* Asynchronously sets the provided user as currentUser on the current Auth
* instance.
Expand Down Expand Up @@ -866,6 +894,17 @@ fireauth.Auth.prototype.signOut = function() {
// Wait for final state to be ready first, otherwise a signed out user could
// come back to life.
var p = this.redirectStateIsReady_.then(function() {
// Clear any cached redirect result on sign out, even if user is already
// signed out. For example, sign in could fail due to account conflict
// error, the error in redirect result should still be cleared. There is
// also the use case where you keep a reference to a signed out user and
// call signedOutUser.linkWithRedirect(provider). Even though the user is
// signed out, getRedirectResult() will resolve with the modified signed
// out user. This could also throw an error
// (provider already linked, etc).
if (self.authEventManager_) {
self.authEventManager_.clearRedirectResult();
}
// Ignore if already signed out.
if (!self.currentUser_()) {
return goog.Promise.resolve();
Expand Down Expand Up @@ -999,7 +1038,7 @@ fireauth.Auth.prototype.initAuthRedirectState_ = function() {
// Wait first for state to be loaded from storage.
return this.authStateLoaded_.then(function() {
// Resolve any pending redirect result.
return self.getRedirectResult();
return self.getRedirectResultWithoutClearing_();
}).thenCatch(function(error) {
// Ignore any error in the process. Redirect could be not supported.
return;
Expand Down
214 changes: 214 additions & 0 deletions packages/auth/test/auth_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6959,6 +6959,155 @@ function testAuth_getRedirectResult_unsupportedEnvironment() {
}


function testAuth_signOut_clearSuccessRedirectResult() {
// Tests getRedirectResult with success event after signOut being called.
fireauth.AuthEventManager.ENABLED = true;
const expectedCred = fireauth.GoogleAuthProvider.credential(
null, 'ACCESS_TOKEN');
// Expected sign in via redirect successful Auth event.
const expectedAuthEvent = new fireauth.AuthEvent(
fireauth.AuthEvent.Type.SIGN_IN_VIA_REDIRECT,
null,
'http://www.example.com/#response',
'SESSION_ID');
// Stub instantiateOAuthSignInHandler.
stubs.replace(
fireauth.AuthEventManager, 'instantiateOAuthSignInHandler',
function(authDomain, apiKey, appName) {
return {
'addAuthEventListener': function(handler) {
// auth1 should be subscribed.
const manager = fireauth.AuthEventManager.getManager(
config3['authDomain'], config3['apiKey'], app1.name);
assertTrue(manager.isSubscribed(auth1));
// In this case run immediately with expected redirect event.
handler(expectedAuthEvent);
asyncTestCase.signal();
},
'initializeAndWait': function() { return goog.Promise.resolve(); },
'shouldBeInitializedEarly': function() { return false; },
'hasVolatileStorage': function() { return false; }
};
});
// Simulate successful finishPopupAndRedirectSignIn.
stubs.replace(
fireauth.Auth.prototype,
'finishPopupAndRedirectSignIn',
function(requestUri, sessionId, postBody) {
assertEquals('http://www.example.com/#response', requestUri);
assertEquals('SESSION_ID', sessionId);
assertNull(postBody);
user1 = new fireauth.AuthUser(
config3, expectedTokenResponse, accountInfo);
expectedPopupResult = {
'user': user1,
'credential': expectedCred,
'additionalUserInfo': expectedAdditionalUserInfo,
'operationType': fireauth.constants.OperationType.SIGN_IN
};
// User 1 should be set here and saved to storage.
auth1.setCurrentUser_(user1);
asyncTestCase.signal();
return currentUserStorageManager.setCurrentUser(user1).then(() => {
return expectedPopupResult;
});
});
let user1, expectedPopupResult;
asyncTestCase.waitForSignals(3);
const pendingRedirectManager = new fireauth.storage.PendingRedirectManager(
config3['apiKey'] + ':' + appId1);
currentUserStorageManager = new fireauth.storage.UserManager(
config3['apiKey'] + ':' + appId1);
app1 = firebase.initializeApp(config3, appId1);
auth1 = app1.auth();
pendingRedirectManager.setPendingStatus().then(() => {
// Verify that the redirect result is resolved before signing out.
const manager = fireauth.AuthEventManager.getManager(
config3['authDomain'], config3['apiKey'], app1.name);
return manager.getRedirectResult().then((result) => {
// Expected result returned.
assertObjectEquals(expectedPopupResult, result);
return auth1.signOut();
}).then(() => {
// signOut should clear the cached redirect result.
return auth1.getRedirectResult();
}).then((resultAfterClearing) => {
fireauth.common.testHelper.assertUserCredentialResponse(
null, null, null, undefined, resultAfterClearing);
asyncTestCase.signal();
});
});
}


function testAuth_signOut_clearErrorRedirectResult() {
// Tests getRedirectResult with error event after signOut being called.
fireauth.AuthEventManager.ENABLED = true;
// The expected error.
const expectedError =
new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR);
// Expected sign in via redirect error Auth event.
const expectedAuthEvent = new fireauth.AuthEvent(
fireauth.AuthEvent.Type.SIGN_IN_VIA_REDIRECT,
null,
null,
null,
expectedError);
// Stub instantiateOAuthSignInHandler.
stubs.replace(
fireauth.AuthEventManager, 'instantiateOAuthSignInHandler',
function(authDomain, apiKey, appName) {
return {
'addAuthEventListener': function(handler) {
// auth1 should be subscribed.
const manager = fireauth.AuthEventManager.getManager(
config3['authDomain'], config3['apiKey'], app1.name);
assertTrue(manager.isSubscribed(auth1));
// In this case run immediately with expected redirect event.
handler(expectedAuthEvent);
asyncTestCase.signal();
},
'initializeAndWait': function() { return goog.Promise.resolve(); },
'shouldBeInitializedEarly': function() { return false; },
'hasVolatileStorage': function() { return false; }
};
});
stubs.replace(
fireauth.Auth.prototype,
'finishPopupAndRedirectSignIn',
function(requestUri, sessionId, postBody) {
fail('finishPopupAndRedirectSignIn should not run on event error!');
});
asyncTestCase.waitForSignals(2);
const pendingRedirectManager = new fireauth.storage.PendingRedirectManager(
config3['apiKey'] + ':' + appId1);
currentUserStorageManager = new fireauth.storage.UserManager(
config3['apiKey'] + ':' + appId1);
app1 = firebase.initializeApp(config3, appId1);
auth1 = app1.auth();
pendingRedirectManager.setPendingStatus().then(() => {
// Verify that the redirect result is rejected with error before
// signing out.
const manager = fireauth.AuthEventManager.getManager(
config3['authDomain'], config3['apiKey'], app1.name);
return manager.getRedirectResult().thenCatch((error) => {
// Expected error returned.
fireauth.common.testHelper.assertErrorEquals(expectedError, error);
return auth1.signOut();
}).then(() => {
// signOut should clear the error in cached redirect result.
return auth1.getRedirectResult();
}).then((resultAfterClearing) => {
fireauth.common.testHelper.assertUserCredentialResponse(
null, null, null, undefined, resultAfterClearing);
asyncTestCase.signal();
});
}).thenCatch((error) => {
fail('Redirect result should be cleared by signOut.');
}) ;
}


function testAuth_returnFromSignInWithRedirect_success_withoutPostBody() {
// Tests the return from a successful sign in with redirect.
fireauth.AuthEventManager.ENABLED = true;
Expand Down Expand Up @@ -7025,6 +7174,11 @@ function testAuth_returnFromSignInWithRedirect_success_withoutPostBody() {
auth1.getRedirectResult().then(function(result) {
// Expected result returned.
assertObjectEquals(expectedPopupResult, result);
// Redirect result should be cleared after being returned once.
return auth1.getRedirectResult();
}).then(function(resultAfterClearing) {
fireauth.common.testHelper.assertUserCredentialResponse(
null, null, null, undefined, resultAfterClearing);
asyncTestCase.signal();
});
});
Expand Down Expand Up @@ -7114,6 +7268,11 @@ function testAuth_returnFromSignInWithRedirect_success_withPostBody() {
auth1.getRedirectResult().then(function(result) {
// Expected result returned.
assertObjectEquals(expectedPopupResult, result);
// Redirect result should be cleared after being returned once.
return auth1.getRedirectResult();
}).then(function(resultAfterClearing) {
fireauth.common.testHelper.assertUserCredentialResponse(
null, null, null, undefined, resultAfterClearing);
asyncTestCase.signal();
});
});
Expand Down Expand Up @@ -7226,6 +7385,11 @@ function testAuth_returnFromSignInWithRedirect_withExistingUser() {
// Newly signed in user.
assertEquals(user1, auth1['currentUser']);
assertObjectEquals(expectedPopupResult, result);
// Redirect result should be cleared after being returned once.
return auth1.getRedirectResult();
}).then(function(resultAfterClearing) {
fireauth.common.testHelper.assertUserCredentialResponse(
null, null, null, undefined, resultAfterClearing);
asyncTestCase.signal();
});
// State listener should fire once only with the final redirected user.
Expand Down Expand Up @@ -7296,7 +7460,14 @@ function testAuth_returnFromSignInWithRedirect_error() {
// Get redirect result should return the expected error.
auth1.getRedirectResult().thenCatch(function(error) {
fireauth.common.testHelper.assertErrorEquals(expectedError, error);
// Error in redirect result should be cleared after being returned once.
return auth1.getRedirectResult();
}).then(function(resultAfterClearing) {
fireauth.common.testHelper.assertUserCredentialResponse(
null, null, null, undefined, resultAfterClearing);
asyncTestCase.signal();
}).thenCatch(function(error) {
fail('Error in event should only be thrown once.');
});
});
// State listener should fire once only with null user.
Expand Down Expand Up @@ -7365,6 +7536,12 @@ function testAuth_returnFromSignInWithRedirect_error_webStorageNotSupported() {
pendingRedirectManager.setPendingStatus().then(function() {
// Get redirect result should return the expected error.
auth1.getRedirectResult().thenCatch(function(error) {
fireauth.common.testHelper.assertErrorEquals(expectedError, error);
// Errors not being tied to a sign-in session should not be cleared.
return auth1.getRedirectResult();
}).then(function(result) {
fail('Errors not being tied to a sign-in session should not be cleared.');
}).thenCatch(function(error) {
fireauth.common.testHelper.assertErrorEquals(expectedError, error);
asyncTestCase.signal();
});
Expand Down Expand Up @@ -7535,6 +7712,11 @@ function testAuth_returnFromLinkWithRedirect_success_withoutPostBody() {
// Get redirect result should resolve with expected user and credential.
auth1.getRedirectResult().then(function(result) {
assertObjectEquals(expectedPopupResult, result);
// Redirect result should be cleared after being returned once.
return auth1.getRedirectResult();
}).then(function(resultAfterClearing) {
fireauth.common.testHelper.assertUserCredentialResponse(
null, null, null, undefined, resultAfterClearing);
asyncTestCase.signal();
});
// Should fire once only with the final redirected user.
Expand Down Expand Up @@ -7636,6 +7818,11 @@ function testAuth_returnFromLinkWithRedirect_success_withPostBody() {
// Get redirect result should resolve with expected user and credential.
auth1.getRedirectResult().then(function(result) {
assertObjectEquals(expectedPopupResult, result);
// Redirect result should be cleared after being returned once.
return auth1.getRedirectResult();
}).then(function(resultAfterClearing) {
fireauth.common.testHelper.assertUserCredentialResponse(
null, null, null, undefined, resultAfterClearing);
asyncTestCase.signal();
});
// Should fire once only with the final redirected user.
Expand Down Expand Up @@ -7779,6 +7966,11 @@ function testAuth_returnFromLinkWithRedirect_redirectedLoggedOutUser_success() {
assertEquals('fr', result.user.getLanguageCode());
auth1.languageCode = 'de';
assertEquals('de', result.user.getLanguageCode());
// Redirect result should be cleared after being returned once.
return auth1.getRedirectResult();
}).then(function(resultAfterClearing) {
fireauth.common.testHelper.assertUserCredentialResponse(
null, null, null, undefined, resultAfterClearing);
asyncTestCase.signal();
});
// Should fire once only with the original user.
Expand Down Expand Up @@ -7889,6 +8081,11 @@ function testAuth_redirectedLoggedOutUser_differentAuthDomain() {
assertEquals('fr', result.user.getLanguageCode());
auth1.languageCode = 'de';
assertEquals('de', result.user.getLanguageCode());
// Redirect result should be cleared after being returned once.
return auth1.getRedirectResult();
}).then(function(resultAfterClearing) {
fireauth.common.testHelper.assertUserCredentialResponse(
null, null, null, undefined, resultAfterClearing);
asyncTestCase.signal();
});
// Should fire once only with null user.
Expand Down Expand Up @@ -8014,6 +8211,11 @@ function testAuth_returnFromLinkWithRedirect_noCurrentUser_redirectUser() {
assertEquals('fr', result.user.getLanguageCode());
auth1.languageCode = 'de';
assertEquals('de', result.user.getLanguageCode());
// Redirect result should be cleared after being returned once.
return auth1.getRedirectResult();
}).then(function(resultAfterClearing) {
fireauth.common.testHelper.assertUserCredentialResponse(
null, null, null, undefined, resultAfterClearing);
asyncTestCase.signal();
});
// Should fire once only with null user.
Expand Down Expand Up @@ -8217,6 +8419,11 @@ function testAuth_returnFromLinkWithRedirect_redirectedLoggedInUser_success() {
// operationType not implemented yet.
fireauth.constants.OperationType.SIGN_IN,
result);
// Redirect result should be cleared after being returned once.
return auth1.getRedirectResult();
}).then(function(resultAfterClearing) {
fireauth.common.testHelper.assertUserCredentialResponse(
null, null, null, undefined, resultAfterClearing);
asyncTestCase.signal();
});
// Should fire once only with redirected user.
Expand Down Expand Up @@ -8402,7 +8609,14 @@ function testAuth_returnFromLinkWithRedirect_error() {
// Get redirect result should contain an error.
auth1.getRedirectResult().thenCatch(function(error) {
fireauth.common.testHelper.assertErrorEquals(expectedError, error);
// Error in redirect result should be cleared after being returned once.
return auth1.getRedirectResult();
}).then(function(resultAfterClearing) {
fireauth.common.testHelper.assertUserCredentialResponse(
null, null, null, undefined, resultAfterClearing);
asyncTestCase.signal();
}).thenCatch(function(error) {
fail('Error in event should only be thrown once.');
});
// Should fire once only with original user1.
var idTokenChangeCounter = 0;
Expand Down

0 comments on commit ec1e9be

Please sign in to comment.