author | Kit Cambridge <kcambridge@mozilla.com> |
Wed, 20 Jan 2016 18:12:22 -0800 | |
changeset 292120 | 3e64199755c5692e10c13068cf96ad5210df3e35 |
parent 292119 | 133d8859d7b5e7d44d74c66aaa52c2da30cab3c4 |
child 292121 | 572875411dbaa092bf066a9ce561da6a2fc1c293 |
push id | 74762 |
push user | cbook@mozilla.com |
push date | Thu, 07 Apr 2016 09:56:20 +0000 |
treeherder | mozilla-inbound@772253c53374 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | markh |
bugs | 1044530 |
milestone | 48.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/services/fxaccounts/FxAccounts.jsm +++ b/services/fxaccounts/FxAccounts.jsm @@ -412,16 +412,20 @@ FxAccountsInternal.prototype = { get localtimeOffsetMsec() { return this.fxAccountsClient.localtimeOffsetMsec; }, /** * Ask the server whether the user's email has been verified */ checkEmailStatus: function checkEmailStatus(sessionToken, options = {}) { + if (!sessionToken) { + return Promise.reject(new Error( + "checkEmailStatus called without a session token")); + } return this.fxAccountsClient.recoveryEmailStatus(sessionToken, options); }, /** * Once the user's email is verified, we can request the keys */ fetchKeys: function fetchKeys(keyFetchToken) { log.debug("fetchKeys: " + !!keyFetchToken); @@ -570,27 +574,29 @@ FxAccountsInternal.prototype = { // No signed-in user return null; } if (!this.isUserEmailVerified(data)) { // Signed-in user has not verified email return null; } if (!data.sessionToken) { - // can't get a signed certificate without a session token, but that - // should be impossible - make log noise about it. - log.error("getAssertion called without a session token!"); - return null; + // can't get a signed certificate without a session token. This + // can happen if we request an assertion after clearing an invalid + // session token from storage. + throw this._error(ERROR_AUTH_ERROR, "getAssertion called without a session token"); } return this.getKeypairAndCertificate(currentState).then( ({keyPair, certificate}) => { return this.getAssertionFromCert(data, keyPair, certificate, audience); } ); - }).then(result => currentState.resolve(result)); + }).catch(err => + this._handleTokenError(err) + ).then(result => currentState.resolve(result)); }, getDeviceId() { return this.currentAccountState.getUserAccountData() .then(data => { if (data) { if (data.isDeviceStale || !data.deviceId) { // A previous device registration attempt failed or there is no @@ -614,18 +620,23 @@ FxAccountsInternal.prototype = { */ resendVerificationEmail: function resendVerificationEmail() { let currentState = this.currentAccountState; return this.getSignedInUser().then(data => { // If the caller is asking for verification to be re-sent, and there is // no signed-in user to begin with, this is probably best regarded as an // error. if (data) { + if (!data.sessionToken) { + return Promise.reject(new Error( + "resendVerificationEmail called without a session token")); + } this.pollEmailStatus(currentState, data.sessionToken, "start"); - return this.fxAccountsClient.resendVerificationEmail(data.sessionToken); + return this.fxAccountsClient.resendVerificationEmail( + data.sessionToken).catch(err => this._handleTokenError(err)); } throw new Error("Cannot resend verification email; no signed-in user"); }); }, /* * Reset state such that any previous flow is canceled. */ @@ -705,17 +716,20 @@ FxAccountsInternal.prototype = { // to FxAccountsClient.signOut(). if (!localOnly) { // Wrap this in a promise so *any* errors in signOut won't // block the local sign out. This is *not* returned. Promise.resolve().then(() => { // This can happen in the background and shouldn't block // the user from signing out. The server must tolerate // clients just disappearing, so this call should be best effort. - return this._signOutServer(sessionToken, deviceId); + if (sessionToken) { + return this._signOutServer(sessionToken, deviceId); + } + log.warn("Missing session token; skipping remote sign out"); }).catch(err => { log.error("Error during remote sign out of Firefox Accounts", err); }).then(() => { return this._destroyAllOAuthTokens(tokensToRevoke); }).catch(err => { log.error("Error during destruction of oauth tokens during signout", err); }).then(() => { // just for testing - notifications are cheap when no observers. @@ -747,18 +761,17 @@ FxAccountsInternal.prototype = { // we must tell the server to either destroy the device or sign out // (if no device exists). We might need to revisit this when this // FxA code is used in a context that isn't Sync. const options = { service: "sync" }; if (deviceId) { log.debug("destroying device and session"); - return this.fxAccountsClient.signOutAndDestroyDevice(sessionToken, deviceId, options) - .then(() => this.currentAccountState.updateUserAccountData({ deviceId: null })); + return this.fxAccountsClient.signOutAndDestroyDevice(sessionToken, deviceId, options); } log.debug("destroying session"); return this.fxAccountsClient.signOut(sessionToken, options); }, /** * Fetch encryption keys for the signed-in-user from the FxA API server. @@ -805,17 +818,19 @@ FxAccountsInternal.prototype = { currentState.whenKeysReadyDeferred.reject(err); } ); } else { currentState.whenKeysReadyDeferred.reject('No keyFetchToken'); } } return currentState.whenKeysReadyDeferred.promise; - }).then(result => currentState.resolve(result)); + }).catch(err => + this._handleTokenError(err) + ).then(result => currentState.resolve(result)); }, fetchAndUnwrapKeys: function(keyFetchToken) { if (logPII) { log.debug("fetchAndUnwrapKeys: token: " + keyFetchToken); } let currentState = this.currentAccountState; return Task.spawn(function* task() { @@ -1110,28 +1125,35 @@ FxAccountsInternal.prototype = { if (error && error.retryAfter) { // If the server told us to back off, back off the requested amount. timeoutMs = (error.retryAfter + 3) * 1000; } // The server will return 401 if a request parameter is erroneous or // if the session token expired. Let's continue polling otherwise. if (!error || !error.code || error.code != 401) { this.pollEmailStatusAgain(currentState, sessionToken, timeoutMs); + } else { + let error = new Error("Verification status check failed"); + this._rejectWhenVerified(currentState, error); } }); }, + _rejectWhenVerified(currentState, error) { + currentState.whenVerifiedDeferred.reject(error); + delete currentState.whenVerifiedDeferred; + }, + // Poll email status using truncated exponential back-off. pollEmailStatusAgain: function (currentState, sessionToken, timeoutMs) { let ageMs = Date.now() - this.pollStartDate; if (ageMs >= this.POLL_SESSION) { if (currentState.whenVerifiedDeferred) { let error = new Error("User email verification timed out."); - currentState.whenVerifiedDeferred.reject(error); - delete currentState.whenVerifiedDeferred; + this._rejectWhenVerified(currentState, error); } log.debug("polling session exceeded, giving up"); return; } if (timeoutMs === undefined) { let currentMinute = Math.ceil(ageMs / 60000); timeoutMs = currentMinute <= 2 ? this.VERIFICATION_POLL_TIMEOUT_INITIAL : this.VERIFICATION_POLL_TIMEOUT_SUBSEQUENT; @@ -1380,17 +1402,18 @@ FxAccountsInternal.prototype = { */ _errorToErrorClass: function (aError) { if (aError.errno) { let error = SERVER_ERRNO_TO_ERROR[aError.errno]; return this._error(ERROR_TO_GENERAL_ERROR_CLASS[error] || ERROR_UNKNOWN, aError); } else if (aError.message && (aError.message === "INVALID_PARAMETER" || aError.message === "NO_ACCOUNT" || - aError.message === "UNVERIFIED_ACCOUNT")) { + aError.message === "UNVERIFIED_ACCOUNT" || + aError.message === "AUTH_ERROR")) { return aError; } return this._error(ERROR_UNKNOWN, aError); }, _error: function(aError, aDetails) { log.error("FxA rejecting with error ${aError}, details: ${aDetails}", {aError, aDetails}); let reason = new Error(aError); @@ -1453,16 +1476,21 @@ FxAccountsInternal.prototype = { // 1. It makes remote requests to the auth server. // 2. _getDeviceName does not work from xpcshell. // 3. The B2G tests fail when attempting to import services-sync/util.js. if (Services.prefs.getBoolPref("identity.fxaccounts.skipDeviceRegistration")) { return Promise.resolve(); } } catch(ignore) {} + if (!signedInUser.sessionToken) { + return Promise.reject(new Error( + "_registerOrUpdateDevice called without a session token")); + } + return this.fxaPushService.registerPushEndpoint().then(subscription => { const deviceName = this._getDeviceName(); let deviceOptions = {}; // if we were able to obtain a subscription if (subscription && subscription.endpoint) { deviceOptions.pushCallback = subscription.endpoint; } @@ -1499,18 +1527,21 @@ FxAccountsInternal.prototype = { return this._recoverFromUnknownDevice(); } if (error.errno === ERRNO_DEVICE_SESSION_CONFLICT) { return this._recoverFromDeviceSessionConflict(error, sessionToken); } } - return this._logErrorAndSetStaleDeviceFlag(error); - }).catch(() => {}); + // `_handleTokenError` re-throws the error. + return this._handleTokenError(error); + }).catch(error => + this._logErrorAndSetStaleDeviceFlag(error) + ).catch(() => {}); }, _recoverFromUnknownDevice() { // FxA did not recognise the device id. Handle it by clearing the device // id on the account data. At next sync or next sign-in, registration is // retried and should succeed. log.warn("unknown device id, clearing the local device data"); return this.currentAccountState.updateUserAccountData({ deviceId: null }) @@ -1556,17 +1587,48 @@ FxAccountsInternal.prototype = { log.error("device registration failed", error); return this.currentAccountState.updateUserAccountData({ isDeviceStale: true }).catch(secondError => { log.error( "failed to set stale device flag, device registration won't be retried", secondError); }).then(() => {}); - } + }, + + _handleTokenError(err) { + if (!err || err.code != 401 || err.errno != ERRNO_INVALID_AUTH_TOKEN) { + throw err; + } + log.warn("recovering from invalid token error", err); + return this.accountStatus().then(exists => { + if (!exists) { + // Delete all local account data. Since the account no longer + // exists, we can skip the remote calls. + log.info("token invalidated because the account no longer exists"); + return this.signOut(true); + } + + // Delete all fields except those required for the user to + // reauthenticate. + log.info("clearing credentials to handle invalid token error"); + let updateData = {}; + let clearField = field => { + if (!FXA_PWDMGR_REAUTH_WHITELIST.has(field)) { + updateData[field] = null; + } + } + FXA_PWDMGR_PLAINTEXT_FIELDS.forEach(clearField); + FXA_PWDMGR_SECURE_FIELDS.forEach(clearField); + FXA_PWDMGR_MEMORY_FIELDS.forEach(clearField); + + let currentState = this.currentAccountState; + return currentState.updateUserAccountData(updateData); + }).then(() => Promise.reject(err)); + }, }; // A getter for the instance to export XPCOMUtils.defineLazyGetter(this, "fxAccounts", function() { let a = new FxAccounts(); // XXX Bug 947061 - We need a strategy for resuming email verification after
--- a/services/fxaccounts/FxAccountsCommon.js +++ b/services/fxaccounts/FxAccountsCommon.js @@ -234,16 +234,21 @@ exports.FXA_PWDMGR_PLAINTEXT_FIELDS = ne // Fields we store in secure storage if it exists. exports.FXA_PWDMGR_SECURE_FIELDS = new Set( ["kA", "kB", "keyFetchToken", "unwrapBKey", "assertion"]); // Fields we keep in memory and don't persist anywhere. exports.FXA_PWDMGR_MEMORY_FIELDS = new Set( ["cert", "keyPair"]); +// A whitelist of fields that remain in storage when the user needs to +// reauthenticate. All other fields will be removed. +exports.FXA_PWDMGR_REAUTH_WHITELIST = new Set( + ["email", "uid", "profile", "deviceId", "isDeviceStale", "verified"]); + // The pseudo-host we use in the login manager exports.FXA_PWDMGR_HOST = "chrome://FirefoxAccounts"; // The realm we use in the login manager. exports.FXA_PWDMGR_REALM = "Firefox Accounts credentials"; // Error matching. exports.SERVER_ERRNO_TO_ERROR = {};
--- a/services/fxaccounts/tests/xpcshell/test_accounts.js +++ b/services/fxaccounts/tests/xpcshell/test_accounts.js @@ -548,16 +548,81 @@ add_test(function test_getKeys() { do_check_eq(user.unwrapBKey, undefined); run_next_test(); }); }); }); }); }); +add_task(function* test_getKeys_nonexistent_account() { + let fxa = new MockFxAccounts(); + let bismarck = getTestUser("bismarck"); + + let client = fxa.internal.fxAccountsClient; + client.accountStatus = () => Promise.resolve(false); + client.accountKeys = () => { + return Promise.reject({ + code: 401, + errno: ERRNO_INVALID_AUTH_TOKEN, + }); + }; + + yield fxa.setSignedInUser(bismarck); + + let promiseLogout = new Promise(resolve => { + makeObserver(ONLOGOUT_NOTIFICATION, function() { + log.debug("test_getKeys_nonexistent_account observed logout"); + resolve(); + }); + }); + + try { + yield fxa.internal.getKeys(); + do_check_true(false); + } catch (err) { + do_check_eq(err.code, 401); + do_check_eq(err.errno, ERRNO_INVALID_AUTH_TOKEN); + } + + yield promiseLogout; + + let user = yield fxa.internal.getUserAccountData(); + do_check_eq(user, null); +}); + +// getKeys with invalid keyFetchToken should delete keyFetchToken from storage +add_task(function* test_getKeys_invalid_token() { + let fxa = new MockFxAccounts(); + let yusuf = getTestUser("yusuf"); + + let client = fxa.internal.fxAccountsClient; + client.accountStatus = () => Promise.resolve(true); + client.accountKeys = () => { + return Promise.reject({ + code: 401, + errno: ERRNO_INVALID_AUTH_TOKEN, + }); + }; + + yield fxa.setSignedInUser(yusuf); + + try { + yield fxa.internal.getKeys(); + do_check_true(false); + } catch (err) { + do_check_eq(err.code, 401); + do_check_eq(err.errno, ERRNO_INVALID_AUTH_TOKEN); + } + + let user = yield fxa.internal.getUserAccountData(); + do_check_eq(user.email, yusuf.email); + do_check_eq(user.keyFetchToken, null); +}); + // fetchAndUnwrapKeys with no keyFetchToken should trigger signOut add_test(function test_fetchAndUnwrapKeys_no_token() { let fxa = new MockFxAccounts(); let user = getTestUser("lettuce.protheroe"); delete user.keyFetchToken makeObserver(ONLOGOUT_NOTIFICATION, function() { log.debug("test_fetchAndUnwrapKeys_no_token observed logout"); @@ -612,16 +677,49 @@ add_test(function test_overlapping_signi log.debug("Bob verifying his email ..."); fxa.internal.fxAccountsClient._verified = true; }); }); }); }); }); +add_task(function* test_getAssertion_invalid_token() { + let fxa = new MockFxAccounts(); + + let client = fxa.internal.fxAccountsClient; + client.accountStatus = () => Promise.resolve(true); + + let creds = { + sessionToken: "sessionToken", + kA: expandHex("11"), + kB: expandHex("66"), + verified: true, + email: "sonia@example.com", + }; + yield fxa.setSignedInUser(creds); + + try { + let promiseAssertion = fxa.getAssertion("audience.example.com"); + fxa.internal._d_signCertificate.reject({ + code: 401, + errno: ERRNO_INVALID_AUTH_TOKEN, + }); + yield promiseAssertion; + do_check_true(false, "getAssertion should reject invalid session token"); + } catch (err) { + do_check_eq(err.code, 401); + do_check_eq(err.errno, ERRNO_INVALID_AUTH_TOKEN); + } + + let user = yield fxa.internal.getUserAccountData(); + do_check_eq(user.email, creds.email); + do_check_eq(user.sessionToken, null); +}); + add_task(function* test_getAssertion() { let fxa = new MockFxAccounts(); do_check_throws(function* () { yield fxa.getAssertion("nonaudience"); }); let creds = { @@ -778,16 +876,49 @@ add_test(function test_accountStatus() { } ) } ); } ); }); +add_task(function* test_resend_email_invalid_token() { + let fxa = new MockFxAccounts(); + let sophia = getTestUser("sophia"); + do_check_neq(sophia.sessionToken, null); + + let client = fxa.internal.fxAccountsClient; + client.resendVerificationEmail = () => { + return Promise.reject({ + code: 401, + errno: ERRNO_INVALID_AUTH_TOKEN, + }); + }; + client.accountStatus = () => Promise.resolve(true); + + yield fxa.setSignedInUser(sophia); + let user = yield fxa.internal.getUserAccountData(); + do_check_eq(user.email, sophia.email); + do_check_eq(user.verified, false); + log.debug("Sophia wants verification email resent"); + + try { + yield fxa.resendVerificationEmail(); + do_check_true(false, "resendVerificationEmail should reject invalid session token"); + } catch (err) { + do_check_eq(err.code, 401); + do_check_eq(err.errno, ERRNO_INVALID_AUTH_TOKEN); + } + + user = yield fxa.internal.getUserAccountData(); + do_check_eq(user.email, sophia.email); + do_check_eq(user.sessionToken, null); +}); + add_test(function test_resend_email() { let fxa = new MockFxAccounts(); let alice = getTestUser("alice"); let initialState = fxa.internal.currentAccountState; // Alice is the user signing in; her email is unverified. fxa.setSignedInUser(alice).then(() => { @@ -913,32 +1044,37 @@ add_task(function* test_sign_out_without }); }); yield fxa.signOut(); yield promise; }); -add_test(function test_sign_out_with_remote_error() { +add_task(function* test_sign_out_with_remote_error() { let fxa = new MockFxAccounts(); let client = fxa.internal.fxAccountsClient; let remoteSignOutCalled = false; // Force remote sign out to trigger an error - client.signOut = function() { remoteSignOutCalled = true; throw "Remote sign out error"; }; - makeObserver(ONLOGOUT_NOTIFICATION, function() { - log.debug("test_sign_out_with_remote_error observed onlogout"); - // user should be undefined after sign out - fxa.internal.getUserAccountData().then(user => { - do_check_eq(user, null); - do_check_true(remoteSignOutCalled); - run_next_test(); + client.signOutAndDestroyDevice = function() { remoteSignOutCalled = true; throw "Remote sign out error"; }; + let promiseLogout = new Promise(resolve => { + makeObserver(ONLOGOUT_NOTIFICATION, function() { + log.debug("test_sign_out_with_remote_error observed onlogout"); + resolve(); }); }); - fxa.signOut(); + + let jane = getTestUser("jane"); + yield fxa.setSignedInUser(jane); + yield fxa.signOut(); + yield promiseLogout; + + let user = yield fxa.internal.getUserAccountData(); + do_check_eq(user, null); + do_check_true(remoteSignOutCalled); }); add_test(function test_getOAuthToken() { let fxa = new MockFxAccounts(); let alice = getTestUser("alice"); alice.verified = true; let getTokenFromAssertionCalled = false;
--- a/services/sync/modules-testing/utils.js +++ b/services/sync/modules-testing/utils.js @@ -6,16 +6,17 @@ this.EXPORTED_SYMBOLS = [ "btoa", // It comes from a module import. "encryptPayload", "isConfiguredWithLegacyIdentity", "ensureLegacyIdentityManager", "setBasicCredentials", "makeIdentityConfig", + "makeFxAccountsInternalMock", "configureFxAccountIdentity", "configureIdentity", "SyncTestingInfrastructure", "waitForZeroTimer", "Promise", // from a module import "add_identity_test", "MockFxaStorageManager", "AccountState", // from a module import @@ -174,43 +175,45 @@ this.makeIdentityConfig = function(overr if (overrides.fxaccount) { // TODO: allow just some attributes to be specified result.fxaccount = overrides.fxaccount; } } return result; } -// Configure an instance of an FxAccount identity provider with the specified -// config (or the default config if not specified). -this.configureFxAccountIdentity = function(authService, - config = makeIdentityConfig()) { - // until we get better test infrastructure for bid_identity, we set the - // signedin user's "email" to the username, simply as many tests rely on this. - config.fxaccount.user.email = config.username; - - let fxa; - let MockInternal = { +this.makeFxAccountsInternalMock = function(config) { + return { newAccountState(credentials) { // We only expect this to be called with null indicating the (mock) // storage should be read. if (credentials) { throw new Error("Not expecting to have credentials passed"); } let storageManager = new MockFxaStorageManager(); storageManager.initialize(config.fxaccount.user); let accountState = new AccountState(storageManager); return accountState; }, _getAssertion(audience) { return Promise.resolve("assertion"); }, + }; +}; - }; - fxa = new FxAccounts(MockInternal); +// Configure an instance of an FxAccount identity provider with the specified +// config (or the default config if not specified). +this.configureFxAccountIdentity = function(authService, + config = makeIdentityConfig(), + fxaInternal = makeFxAccountsInternalMock(config)) { + // until we get better test infrastructure for bid_identity, we set the + // signedin user's "email" to the username, simply as many tests rely on this. + config.fxaccount.user.email = config.username; + + let fxa = new FxAccounts(fxaInternal); let MockFxAccountsClient = function() { FxAccountsClient.apply(this); }; MockFxAccountsClient.prototype = { __proto__: FxAccountsClient.prototype, accountStatus() { return Promise.resolve(true);
--- a/services/sync/modules/browserid_identity.js +++ b/services/sync/modules/browserid_identity.js @@ -240,32 +240,21 @@ this.BrowserIDManager.prototype = { this._log.info("Background fetch for key bundle done"); Weave.Status.login = LOGIN_SUCCEEDED; if (isInitialSync) { this._log.info("Doing initial sync actions"); Svc.Prefs.set("firstSync", "resetClient"); Services.obs.notifyObservers(null, "weave:service:setup-complete", null); Weave.Utils.nextTick(Weave.Service.sync, Weave.Service); } - }).catch(err => { - let authErr = err; // note that we must reject with this error and not a - // subsequent one + }).catch(authErr => { // report what failed... this._log.error("Background fetch for key bundle failed", authErr); - // check if the account still exists - this._fxaService.accountStatus().then(exists => { - if (!exists) { - return fxAccounts.signOut(true); - } - }).catch(err => { - this._log.error("Error while trying to determine FXA existence", err); - }).then(() => { - this._shouldHaveSyncKeyBundle = true; // but we probably don't have one... - this.whenReadyToAuthenticate.reject(authErr) - }); + this._shouldHaveSyncKeyBundle = true; // but we probably don't have one... + this.whenReadyToAuthenticate.reject(authErr); }); // and we are done - the fetch continues on in the background... }).catch(err => { this._log.error("Processing logged in account", err); }); }, _updateSignedInUser: function(userData) { @@ -621,16 +610,19 @@ this.BrowserIDManager.prototype = { // TODO: unify these errors - we need to handle errors thrown by // both tokenserverclient and hawkclient. // A tokenserver error thrown based on a bad response. if (err.response && err.response.status === 401) { err = new AuthenticationError(err); // A hawkclient error. } else if (err.code && err.code === 401) { err = new AuthenticationError(err); + // An FxAccounts.jsm error. + } else if (err.message == fxAccountsCommon.ERROR_AUTH_ERROR) { + err = new AuthenticationError(err); } // TODO: write tests to make sure that different auth error cases are handled here // properly: auth error getting assertion, auth error getting token (invalid generation // and client-state error) if (err instanceof AuthenticationError) { this._log.error("Authentication error in _fetchTokenForUser", err); // set it to the "fatal" LOGIN_FAILED_LOGIN_REJECTED reason.
--- a/services/sync/tests/unit/test_browserid_identity.js +++ b/services/sync/tests/unit/test_browserid_identity.js @@ -84,52 +84,61 @@ add_task(function* test_initialializeWit do_check_true(browseridManager.hasValidToken()); do_check_eq(browseridManager.account, identityConfig.fxaccount.user.email); } ); add_task(function* test_initialializeWithAuthErrorAndDeletedAccount() { _("Verify sync unpair after initializeWithCurrentIdentity with auth error + account deleted"); + var identityConfig = makeIdentityConfig(); + var browseridManager = new BrowserIDManager(); + + // Use the real `_getAssertion` method that calls + // `mockFxAClient.signCertificate`. + let fxaInternal = makeFxAccountsInternalMock(identityConfig); + delete fxaInternal._getAssertion; + + configureFxAccountIdentity(browseridManager, identityConfig, fxaInternal); browseridManager._fxaService.internal.initialize(); - let fetchTokenForUserCalled = false; + let signCertificateCalled = false; let accountStatusCalled = false; let MockFxAccountsClient = function() { FxAccountsClient.apply(this); }; MockFxAccountsClient.prototype = { __proto__: FxAccountsClient.prototype, + signCertificate() { + signCertificateCalled = true; + return Promise.reject({ + code: 401, + errno: ERRNO_INVALID_AUTH_TOKEN, + }); + }, accountStatus() { accountStatusCalled = true; return Promise.resolve(false); } }; let mockFxAClient = new MockFxAccountsClient(); browseridManager._fxaService.internal._fxAccountsClient = mockFxAClient; - let oldFetchTokenForUser = browseridManager._fetchTokenForUser; - browseridManager._fetchTokenForUser = function() { - fetchTokenForUserCalled = true; - return Promise.reject(false); - } - yield browseridManager.initializeWithCurrentIdentity(); yield Assert.rejects(browseridManager.whenReadyToAuthenticate.promise, "should reject due to an auth error"); - do_check_true(fetchTokenForUserCalled); + do_check_true(signCertificateCalled); do_check_true(accountStatusCalled); do_check_false(browseridManager.account); do_check_false(browseridManager._token); do_check_false(browseridManager.hasValidToken()); do_check_false(browseridManager.account); - browseridManager._fetchTokenForUser = oldFetchTokenForUser; }); add_task(function* test_initialializeWithNoKeys() { _("Verify start after initializeWithCurrentIdentity without kA, kB or keyFetchToken"); let identityConfig = makeIdentityConfig(); delete identityConfig.fxaccount.user.kA; delete identityConfig.fxaccount.user.kB; // there's no keyFetchToken by default, so the initialize should fail.