author | Wes Kocher <wkocher@mozilla.com> |
Tue, 29 Dec 2015 14:43:04 -0800 | |
changeset 277876 | 0c12d4229be0068eb1b9beb5064b800f12143f20 |
parent 277875 | ab22ba41b8fce8eaf15ed4128f6c978b20b8b238 |
child 277877 | 3ba655f6bc67660a2dcfc4c2a5b3d0d17714f53d |
push id | 69628 |
push user | cbook@mozilla.com |
push date | Wed, 30 Dec 2015 11:16:09 +0000 |
treeherder | mozilla-inbound@b493cf33851f [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
bugs | 1227527 |
milestone | 46.0a1 |
backs out | 4228fd8ef72fd99cafe4826473593b4ffb81b273 |
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/b2g/components/test/unit/test_fxaccounts.js +++ b/b2g/components/test/unit/test_fxaccounts.js @@ -19,35 +19,31 @@ XPCOMUtils.defineLazyModuleGetter(this, // At end of test, restore original state const ORIGINAL_AUTH_URI = Services.prefs.getCharPref("identity.fxaccounts.auth.uri"); var { SystemAppProxy } = Cu.import("resource://gre/modules/FxAccountsMgmtService.jsm"); const ORIGINAL_SENDCUSTOM = SystemAppProxy._sendCustomEvent; do_register_cleanup(function() { Services.prefs.setCharPref("identity.fxaccounts.auth.uri", ORIGINAL_AUTH_URI); SystemAppProxy._sendCustomEvent = ORIGINAL_SENDCUSTOM; - Services.prefs.clearUserPref("identity.fxaccounts.skipDeviceRegistration"); }); // Make profile available so that fxaccounts can store user data do_get_profile(); // Mock the system app proxy; make message passing possible var mockSendCustomEvent = function(aEventName, aMsg) { Services.obs.notifyObservers({wrappedJSObject: aMsg}, aEventName, null); }; function run_test() { run_next_test(); } add_task(function test_overall() { - // FxA device registration throws from this context - Services.prefs.setBoolPref("identity.fxaccounts.skipDeviceRegistration", true); - do_check_neq(FxAccountsMgmtService, null); }); // Check that invalid email capitalization is corrected on signIn. // https://github.com/mozilla/fxa-auth-server/blob/master/docs/api.md#post-v1accountlogin add_test(function test_invalidEmailCase_signIn() { do_test_pending(); let clientEmail = "greta.garbo@gmail.com"; @@ -104,19 +100,16 @@ add_test(function test_invalidEmailCase_ }); return; }, }); // Point the FxAccountsClient's hawk rest request client to the mock server Services.prefs.setCharPref("identity.fxaccounts.auth.uri", server.baseURI); - // FxA device registration throws from this context - Services.prefs.setBoolPref("identity.fxaccounts.skipDeviceRegistration", true); - // Receive a mozFxAccountsChromeEvent message function onMessage(subject, topic, data) { let message = subject.wrappedJSObject; switch (message.id) { // When we signed in as "Greta.Garbo", the server should have told us // that the proper capitalization is really "greta.garbo". Call // getAccounts to get the signed-in user and ensure that the @@ -166,19 +159,16 @@ add_test(function test_invalidEmailCase_ }, }, }); }); add_test(function testHandleGetAssertionError_defaultCase() { do_test_pending(); - // FxA device registration throws from this context - Services.prefs.setBoolPref("identity.fxaccounts.skipDeviceRegistration", true); - FxAccountsManager.getAssertion(null).then( success => { // getAssertion should throw with invalid audience ok(false); }, reason => { equal("INVALID_AUDIENCE", reason.error); do_test_finished();
--- a/services/fxaccounts/FxAccounts.jsm +++ b/services/fxaccounts/FxAccounts.jsm @@ -25,42 +25,37 @@ XPCOMUtils.defineLazyModuleGetter(this, "resource://gre/modules/identity/jwcrypto.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "FxAccountsOAuthGrantClient", "resource://gre/modules/FxAccountsOAuthGrantClient.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "FxAccountsProfile", "resource://gre/modules/FxAccountsProfile.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Utils", - "resource://services-sync/util.js"); - // All properties exposed by the public FxAccounts API. var publicProperties = [ "accountStatus", "getAccountsClient", "getAccountsSignInURI", "getAccountsSignUpURI", "getAssertion", - "getDeviceId", "getKeys", "getSignedInUser", "getOAuthToken", "getSignedInUserProfile", "loadAndPoll", "localtimeOffsetMsec", "now", "promiseAccountsForceSigninURI", "promiseAccountsChangeProfileURI", "promiseAccountsManageURI", "removeCachedOAuthToken", "resendVerificationEmail", "setSignedInUser", "signOut", - "updateDeviceRegistration", "whenVerified" ]; // An AccountState object holds all state related to one specific account. // Only one AccountState is ever "current" in the FxAccountsInternal object - // whenever a user logs out or logs in, the current AccountState is discarded, // making it impossible for the wrong state or state data to be accidentally // used. @@ -490,19 +485,17 @@ FxAccountsInternal.prototype = { return this.abortExistingFlow().then(() => { let currentAccountState = this.currentAccountState = this.newAccountState( Cu.cloneInto(credentials, {}) // Pass a clone of the credentials object. ); // This promise waits for storage, but not for verification. // We're telling the caller that this is durable now (although is that // really something we should commit to? Why not let the write happen in // the background? Already does for updateAccountData ;) - return currentAccountState.promiseInitialized.then(() => - this.updateDeviceRegistration() - ).then(() => { + return currentAccountState.promiseInitialized.then(() => { Services.telemetry.getHistogramById("FXA_CONFIGURED").add(1); this.notifyObservers(ONLOGIN_NOTIFICATION); if (!this.isUserEmailVerified(credentials)) { this.startVerifiedCheck(credentials); } }).then(() => { return currentAccountState.resolve(); }); @@ -541,36 +534,16 @@ FxAccountsInternal.prototype = { return this.getKeypairAndCertificate(currentState).then( ({keyPair, certificate}) => { return this.getAssertionFromCert(data, keyPair, certificate, audience); } ); }).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 - // device id. Either way, we should register the device with FxA - // before returning the id to the caller. - return this._registerOrUpdateDevice(data); - } - - // Return the device id that we already registered with the server. - return data.deviceId; - } - - // Without a signed-in user, there can be no device id. - return null; - }); - }, - /** * Resend the verification email fot the currently signed-in user. * */ 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 @@ -627,37 +600,32 @@ FxAccountsInternal.prototype = { } return Promise.all(promises); }, signOut: function signOut(localOnly) { let currentState = this.currentAccountState; let sessionToken; let tokensToRevoke; - let deviceId; return currentState.getUserAccountData().then(data => { - // Save the session token, tokens to revoke and the - // device id for use in the call to signOut below. - if (data) { - sessionToken = data.sessionToken; - tokensToRevoke = data.oauthTokens; - deviceId = data.deviceId; - } + // Save the session token for use in the call to signOut below. + sessionToken = data && data.sessionToken; + tokensToRevoke = data && data.oauthTokens; return this._signOutLocal(); }).then(() => { // FxAccountsManager calls here, then does its own call // 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); + return this._signOutServer(sessionToken); }).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. @@ -679,32 +647,21 @@ FxAccountsInternal.prototype = { // this "aborts" this.currentAccountState but doesn't make a new one. return this.abortExistingFlow(); }).then(() => { this.currentAccountState = this.newAccountState(); return this.currentAccountState.promiseInitialized; }); }, - _signOutServer(sessionToken, deviceId) { - // For now we assume the service being logged out from is Sync, so - // 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 })); - } - - log.debug("destroying session"); - return this.fxAccountsClient.signOut(sessionToken, options); + _signOutServer: function signOutServer(sessionToken) { + // For now we assume the service being logged out from is Sync - we might + // need to revisit this when this FxA code is used in a context that + // isn't Sync. + return this.fxAccountsClient.signOut(sessionToken, {service: "sync"}); }, /** * Fetch encryption keys for the signed-in-user from the FxA API server. * * Not for user consumption. Exists to cause the keys to be fetch. * * Returns user data so that it can be chained with other methods. @@ -1372,137 +1329,16 @@ FxAccountsInternal.prototype = { return currentState.resolve(profile); }, error => { log.error("Could not retrieve profile data", error); return currentState.reject(error); } ).catch(err => Promise.reject(this._errorToErrorClass(err))); }, - - // Attempt to update the auth server with whatever device details are stored - // in the account data. Returns a promise that always resolves, never rejects. - // If the promise resolves to a value, that value is the device id. - updateDeviceRegistration() { - return this.getSignedInUser().then(signedInUser => { - if (signedInUser) { - return this._registerOrUpdateDevice(signedInUser); - } - }).catch(error => this._logErrorAndSetStaleDeviceFlag(error)); - }, - - _registerOrUpdateDevice(signedInUser) { - try { - // Allow tests to skip device registration because: - // 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) {} - - return Promise.resolve().then(() => { - const deviceName = this._getDeviceName(); - - if (signedInUser.deviceId) { - log.debug("updating existing device details"); - return this.fxAccountsClient.updateDevice( - signedInUser.sessionToken, signedInUser.deviceId, deviceName); - } - - log.debug("registering new device details"); - return this.fxAccountsClient.registerDevice( - signedInUser.sessionToken, deviceName, this._getDeviceType()); - }).then(device => - this.currentAccountState.updateUserAccountData({ - deviceId: device.id, - isDeviceStale: null - }).then(() => device.id) - ).catch(error => this._handleDeviceError(error, signedInUser.sessionToken)); - }, - - _getDeviceName() { - return Utils.getDeviceName(); - }, - - _getDeviceType() { - return Utils.getDeviceType(); - }, - - _handleDeviceError(error, sessionToken) { - return Promise.resolve().then(() => { - if (error.code === 400) { - if (error.errno === ERRNO_UNKNOWN_DEVICE) { - return this._recoverFromUnknownDevice(); - } - - if (error.errno === ERRNO_DEVICE_SESSION_CONFLICT) { - return this._recoverFromDeviceSessionConflict(error, sessionToken); - } - } - - return 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 }) - .catch(error => this._logErrorAndSetStaleDeviceFlag(error)); - }, - - _recoverFromDeviceSessionConflict(error, sessionToken) { - // FxA has already associated this session with a different device id. - // Perhaps we were beaten in a race to register. Handle the conflict: - // 1. Fetch the list of devices for the current user from FxA. - // 2. Look for ourselves in the list. - // 3. If we find a match, set the correct device id and the stale device - // flag on the account data and return the correct device id. At next - // sync or next sign-in, registration is retried and should succeed. - // 4. If we don't find a match, log the original error. - log.warn("device session conflict, attempting to ascertain the correct device id"); - return this.fxAccountsClient.getDeviceList(sessionToken) - .then(devices => { - const matchingDevices = devices.filter(device => device.isCurrentDevice); - const length = matchingDevices.length; - if (length === 1) { - const deviceId = matchingDevices[0].id - return this.currentAccountState.updateUserAccountData({ - deviceId, - isDeviceStale: true - }).then(() => deviceId); - } - if (length > 1) { - log.error("insane server state, " + length + " devices for this session"); - } - return this._logErrorAndSetStaleDeviceFlag(error); - }).catch(secondError => { - log.error("failed to recover from device-session conflict", secondError); - this._logErrorAndSetStaleDeviceFlag(error) - }); - }, - - _logErrorAndSetStaleDeviceFlag(error) { - // Device registration should never cause other operations to fail. - // If we've reached this point, just log the error and set the stale - // device flag on the account data. At next sync or next sign-in, - // registration will be retried. - 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(() => {}); - } }; // 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/FxAccountsClient.jsm +++ b/services/fxaccounts/FxAccountsClient.jsm @@ -199,17 +199,17 @@ this.FxAccountsClient.prototype = { * * @param sessionTokenHex * The session token encoded in hex * @return Promise */ signOut: function (sessionTokenHex, options = {}) { let path = "/session/destroy"; if (options.service) { - path += "?service=" + encodeURIComponent(options.service); + path += "?service=" + options.service; } return this._request(path, "POST", deriveHawkCredentials(sessionTokenHex, "sessionToken")); }, /** * Check the verification status of the user's FxA email address * @@ -354,126 +354,16 @@ this.FxAccountsClient.prototype = { }, (error) => { log.error("accountStatus failed with: " + error); return Promise.reject(error); } ); }, - /** - * Register a new device - * - * @method registerDevice - * @param sessionTokenHex - * Session token obtained from signIn - * @param name - * Device name - * @param type - * Device type (mobile|desktop) - * @return Promise - * Resolves to an object: - * { - * id: Device identifier - * createdAt: Creation time (milliseconds since epoch) - * name: Name of device - * type: Type of device (mobile|desktop) - * } - */ - registerDevice(sessionTokenHex, name, type) { - let path = "/account/device"; - - let creds = deriveHawkCredentials(sessionTokenHex, "sessionToken"); - let body = { name, type }; - - return this._request(path, "POST", creds, body); - }, - - /** - * Update the session or name for an existing device - * - * @method updateDevice - * @param sessionTokenHex - * Session token obtained from signIn - * @param id - * Device identifier - * @param name - * Device name - * @return Promise - * Resolves to an object: - * { - * id: Device identifier - * name: Device name - * } - */ - updateDevice(sessionTokenHex, id, name) { - let path = "/account/device"; - - let creds = deriveHawkCredentials(sessionTokenHex, "sessionToken"); - let body = { id, name }; - - return this._request(path, "POST", creds, body); - }, - - /** - * Delete a device and its associated session token, signing the user - * out of the server. - * - * @method signOutAndDestroyDevice - * @param sessionTokenHex - * Session token obtained from signIn - * @param id - * Device identifier - * @param [options] - * Options object - * @param [options.service] - * `service` query parameter - * @return Promise - * Resolves to an empty object: - * {} - */ - signOutAndDestroyDevice(sessionTokenHex, id, options={}) { - let path = "/account/device/destroy"; - - if (options.service) { - path += "?service=" + encodeURIComponent(options.service); - } - - let creds = deriveHawkCredentials(sessionTokenHex, "sessionToken"); - let body = { id }; - - return this._request(path, "POST", creds, body); - }, - - /** - * Get a list of currently registered devices - * - * @method getDeviceList - * @param sessionTokenHex - * Session token obtained from signIn - * @return Promise - * Resolves to an array of objects: - * [ - * { - * id: Device id - * isCurrentDevice: Boolean indicating whether the item - * represents the current device - * name: Device name - * type: Device type (mobile|desktop) - * }, - * ... - * ] - */ - getDeviceList(sessionTokenHex) { - let path = "/account/devices"; - let creds = deriveHawkCredentials(sessionTokenHex, "sessionToken"); - - return this._request(path, "GET", creds, {}); - }, - _clearBackoff: function() { this.backoffError = null; }, /** * A general method for sending raw API calls to the FxA auth server. * All request bodies and responses are JSON. *
--- a/services/fxaccounts/FxAccountsCommon.js +++ b/services/fxaccounts/FxAccountsCommon.js @@ -119,20 +119,16 @@ exports.ERRNO_MISSING_CONTENT_LENGTH exports.ERRNO_REQUEST_BODY_TOO_LARGE = 113; exports.ERRNO_TOO_MANY_CLIENT_REQUESTS = 114; exports.ERRNO_INVALID_AUTH_NONCE = 115; exports.ERRNO_ENDPOINT_NO_LONGER_SUPPORTED = 116; exports.ERRNO_INCORRECT_LOGIN_METHOD = 117; exports.ERRNO_INCORRECT_KEY_RETRIEVAL_METHOD = 118; exports.ERRNO_INCORRECT_API_VERSION = 119; exports.ERRNO_INCORRECT_EMAIL_CASE = 120; -exports.ERRNO_ACCOUNT_LOCKED = 121; -exports.ERRNO_ACCOUNT_UNLOCKED = 122; -exports.ERRNO_UNKNOWN_DEVICE = 123; -exports.ERRNO_DEVICE_SESSION_CONFLICT = 124; exports.ERRNO_SERVICE_TEMP_UNAVAILABLE = 201; exports.ERRNO_PARSE = 997; exports.ERRNO_NETWORK = 998; exports.ERRNO_UNKNOWN_ERROR = 999; // Offset oauth server errnos so they don't conflict with auth server errnos exports.OAUTH_SERVER_ERRNO_OFFSET = 1000; @@ -149,20 +145,17 @@ exports.ERRNO_INVALID_REQUEST_PARAM exports.ERRNO_INVALID_RESPONSE_TYPE = 110 + exports.OAUTH_SERVER_ERRNO_OFFSET; exports.ERRNO_UNAUTHORIZED = 111 + exports.OAUTH_SERVER_ERRNO_OFFSET; exports.ERRNO_FORBIDDEN = 112 + exports.OAUTH_SERVER_ERRNO_OFFSET; exports.ERRNO_INVALID_CONTENT_TYPE = 113 + exports.OAUTH_SERVER_ERRNO_OFFSET; // Errors. exports.ERROR_ACCOUNT_ALREADY_EXISTS = "ACCOUNT_ALREADY_EXISTS"; exports.ERROR_ACCOUNT_DOES_NOT_EXIST = "ACCOUNT_DOES_NOT_EXIST "; -exports.ERROR_ACCOUNT_LOCKED = "ACCOUNT_LOCKED"; -exports.ERROR_ACCOUNT_UNLOCKED = "ACCOUNT_UNLOCKED"; exports.ERROR_ALREADY_SIGNED_IN_USER = "ALREADY_SIGNED_IN_USER"; -exports.ERROR_DEVICE_SESSION_CONFLICT = "DEVICE_SESSION_CONFLICT"; exports.ERROR_ENDPOINT_NO_LONGER_SUPPORTED = "ENDPOINT_NO_LONGER_SUPPORTED"; exports.ERROR_INCORRECT_API_VERSION = "INCORRECT_API_VERSION"; exports.ERROR_INCORRECT_EMAIL_CASE = "INCORRECT_EMAIL_CASE"; exports.ERROR_INCORRECT_KEY_RETRIEVAL_METHOD = "INCORRECT_KEY_RETRIEVAL_METHOD"; exports.ERROR_INCORRECT_LOGIN_METHOD = "INCORRECT_LOGIN_METHOD"; exports.ERROR_INVALID_EMAIL = "INVALID_EMAIL"; exports.ERROR_INVALID_AUDIENCE = "INVALID_AUDIENCE"; exports.ERROR_INVALID_AUTH_TOKEN = "INVALID_AUTH_TOKEN"; @@ -186,17 +179,16 @@ exports.ERROR_SERVER_ERROR exports.ERROR_SYNC_DISABLED = "SYNC_DISABLED"; exports.ERROR_TOO_MANY_CLIENT_REQUESTS = "TOO_MANY_CLIENT_REQUESTS"; exports.ERROR_SERVICE_TEMP_UNAVAILABLE = "SERVICE_TEMPORARY_UNAVAILABLE"; exports.ERROR_UI_ERROR = "UI_ERROR"; exports.ERROR_UI_REQUEST = "UI_REQUEST"; exports.ERROR_PARSE = "PARSE_ERROR"; exports.ERROR_NETWORK = "NETWORK_ERROR"; exports.ERROR_UNKNOWN = "UNKNOWN_ERROR"; -exports.ERROR_UNKNOWN_DEVICE = "UNKNOWN_DEVICE"; exports.ERROR_UNVERIFIED_ACCOUNT = "UNVERIFIED_ACCOUNT"; // OAuth errors. exports.ERROR_UNKNOWN_CLIENT_ID = "UNKNOWN_CLIENT_ID"; exports.ERROR_INCORRECT_CLIENT_SECRET = "INCORRECT_CLIENT_SECRET"; exports.ERROR_INCORRECT_REDIRECT_URI = "INCORRECT_REDIRECT_URI"; exports.ERROR_INVALID_FXA_ASSERTION = "INVALID_FXA_ASSERTION"; exports.ERROR_UNKNOWN_CODE = "UNKNOWN_CODE"; @@ -221,18 +213,17 @@ exports.ERROR_MSG_METHOD_NOT_ALLOWED // FxAccounts has the ability to "split" the credentials between a plain-text // JSON file in the profile dir and in the login manager. // In order to prevent new fields accidentally ending up in the "wrong" place, // all fields stored are listed here. // The fields we save in the plaintext JSON. // See bug 1013064 comments 23-25 for why the sessionToken is "safe" exports.FXA_PWDMGR_PLAINTEXT_FIELDS = new Set( - ["email", "verified", "authAt", "sessionToken", "uid", "oauthTokens", "profile", - "deviceId", "isDeviceStale"]); + ["email", "verified", "authAt", "sessionToken", "uid", "oauthTokens", "profile"]); // 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"]); @@ -271,20 +262,16 @@ SERVER_ERRNO_TO_ERROR[ERRNO_MISSING_CONT SERVER_ERRNO_TO_ERROR[ERRNO_REQUEST_BODY_TOO_LARGE] = ERROR_REQUEST_BODY_TOO_LARGE; SERVER_ERRNO_TO_ERROR[ERRNO_TOO_MANY_CLIENT_REQUESTS] = ERROR_TOO_MANY_CLIENT_REQUESTS; SERVER_ERRNO_TO_ERROR[ERRNO_INVALID_AUTH_NONCE] = ERROR_INVALID_AUTH_NONCE; SERVER_ERRNO_TO_ERROR[ERRNO_ENDPOINT_NO_LONGER_SUPPORTED] = ERROR_ENDPOINT_NO_LONGER_SUPPORTED; SERVER_ERRNO_TO_ERROR[ERRNO_INCORRECT_LOGIN_METHOD] = ERROR_INCORRECT_LOGIN_METHOD; SERVER_ERRNO_TO_ERROR[ERRNO_INCORRECT_KEY_RETRIEVAL_METHOD] = ERROR_INCORRECT_KEY_RETRIEVAL_METHOD; SERVER_ERRNO_TO_ERROR[ERRNO_INCORRECT_API_VERSION] = ERROR_INCORRECT_API_VERSION; SERVER_ERRNO_TO_ERROR[ERRNO_INCORRECT_EMAIL_CASE] = ERROR_INCORRECT_EMAIL_CASE; -SERVER_ERRNO_TO_ERROR[ERRNO_ACCOUNT_LOCKED] = ERROR_ACCOUNT_LOCKED; -SERVER_ERRNO_TO_ERROR[ERRNO_ACCOUNT_UNLOCKED] = ERROR_ACCOUNT_UNLOCKED; -SERVER_ERRNO_TO_ERROR[ERRNO_UNKNOWN_DEVICE] = ERROR_UNKNOWN_DEVICE; -SERVER_ERRNO_TO_ERROR[ERRNO_DEVICE_SESSION_CONFLICT] = ERROR_DEVICE_SESSION_CONFLICT; SERVER_ERRNO_TO_ERROR[ERRNO_SERVICE_TEMP_UNAVAILABLE] = ERROR_SERVICE_TEMP_UNAVAILABLE; SERVER_ERRNO_TO_ERROR[ERRNO_UNKNOWN_ERROR] = ERROR_UNKNOWN; SERVER_ERRNO_TO_ERROR[ERRNO_NETWORK] = ERROR_NETWORK; // oauth SERVER_ERRNO_TO_ERROR[ERRNO_UNKNOWN_CLIENT_ID] = ERROR_UNKNOWN_CLIENT_ID; SERVER_ERRNO_TO_ERROR[ERRNO_INCORRECT_CLIENT_SECRET] = ERROR_INCORRECT_CLIENT_SECRET; SERVER_ERRNO_TO_ERROR[ERRNO_INCORRECT_REDIRECT_URI] = ERROR_INCORRECT_REDIRECT_URI; @@ -298,20 +285,17 @@ SERVER_ERRNO_TO_ERROR[ERRNO_INVALID_RESP SERVER_ERRNO_TO_ERROR[ERRNO_UNAUTHORIZED] = ERROR_UNAUTHORIZED; SERVER_ERRNO_TO_ERROR[ERRNO_FORBIDDEN] = ERROR_FORBIDDEN; SERVER_ERRNO_TO_ERROR[ERRNO_INVALID_CONTENT_TYPE] = ERROR_INVALID_CONTENT_TYPE; // Map internal errors to more generic error classes for consumers ERROR_TO_GENERAL_ERROR_CLASS[ERROR_ACCOUNT_ALREADY_EXISTS] = ERROR_AUTH_ERROR; ERROR_TO_GENERAL_ERROR_CLASS[ERROR_ACCOUNT_DOES_NOT_EXIST] = ERROR_AUTH_ERROR; -ERROR_TO_GENERAL_ERROR_CLASS[ERROR_ACCOUNT_LOCKED] = ERROR_AUTH_ERROR; -ERROR_TO_GENERAL_ERROR_CLASS[ERROR_ACCOUNT_UNLOCKED] = ERROR_AUTH_ERROR; ERROR_TO_GENERAL_ERROR_CLASS[ERROR_ALREADY_SIGNED_IN_USER] = ERROR_AUTH_ERROR; -ERROR_TO_GENERAL_ERROR_CLASS[ERROR_DEVICE_SESSION_CONFLICT] = ERROR_AUTH_ERROR; ERROR_TO_GENERAL_ERROR_CLASS[ERROR_ENDPOINT_NO_LONGER_SUPPORTED] = ERROR_AUTH_ERROR; ERROR_TO_GENERAL_ERROR_CLASS[ERROR_INCORRECT_API_VERSION] = ERROR_AUTH_ERROR; ERROR_TO_GENERAL_ERROR_CLASS[ERROR_INCORRECT_EMAIL_CASE] = ERROR_AUTH_ERROR; ERROR_TO_GENERAL_ERROR_CLASS[ERROR_INCORRECT_KEY_RETRIEVAL_METHOD] = ERROR_AUTH_ERROR; ERROR_TO_GENERAL_ERROR_CLASS[ERROR_INCORRECT_LOGIN_METHOD] = ERROR_AUTH_ERROR; ERROR_TO_GENERAL_ERROR_CLASS[ERROR_INVALID_EMAIL] = ERROR_AUTH_ERROR; ERROR_TO_GENERAL_ERROR_CLASS[ERROR_INVALID_AUDIENCE] = ERROR_AUTH_ERROR; ERROR_TO_GENERAL_ERROR_CLASS[ERROR_INVALID_AUTH_TOKEN] = ERROR_AUTH_ERROR; @@ -325,17 +309,16 @@ ERROR_TO_GENERAL_ERROR_CLASS[ERROR_INVAL ERROR_TO_GENERAL_ERROR_CLASS[ERROR_INTERNAL_INVALID_USER] = ERROR_AUTH_ERROR; ERROR_TO_GENERAL_ERROR_CLASS[ERROR_MISSING_BODY_PARAMETERS] = ERROR_AUTH_ERROR; ERROR_TO_GENERAL_ERROR_CLASS[ERROR_MISSING_CONTENT_LENGTH] = ERROR_AUTH_ERROR; ERROR_TO_GENERAL_ERROR_CLASS[ERROR_NO_TOKEN_SESSION] = ERROR_AUTH_ERROR; ERROR_TO_GENERAL_ERROR_CLASS[ERROR_NO_SILENT_REFRESH_AUTH] = ERROR_AUTH_ERROR; ERROR_TO_GENERAL_ERROR_CLASS[ERROR_NOT_VALID_JSON_BODY] = ERROR_AUTH_ERROR; ERROR_TO_GENERAL_ERROR_CLASS[ERROR_PERMISSION_DENIED] = ERROR_AUTH_ERROR; ERROR_TO_GENERAL_ERROR_CLASS[ERROR_REQUEST_BODY_TOO_LARGE] = ERROR_AUTH_ERROR; -ERROR_TO_GENERAL_ERROR_CLASS[ERROR_UNKNOWN_DEVICE] = ERROR_AUTH_ERROR; ERROR_TO_GENERAL_ERROR_CLASS[ERROR_UNVERIFIED_ACCOUNT] = ERROR_AUTH_ERROR; ERROR_TO_GENERAL_ERROR_CLASS[ERROR_UI_ERROR] = ERROR_AUTH_ERROR; ERROR_TO_GENERAL_ERROR_CLASS[ERROR_UI_REQUEST] = ERROR_AUTH_ERROR; ERROR_TO_GENERAL_ERROR_CLASS[ERROR_OFFLINE] = ERROR_NETWORK; ERROR_TO_GENERAL_ERROR_CLASS[ERROR_SERVER_ERROR] = ERROR_NETWORK; ERROR_TO_GENERAL_ERROR_CLASS[ERROR_TOO_MANY_CLIENT_REQUESTS] = ERROR_NETWORK; ERROR_TO_GENERAL_ERROR_CLASS[ERROR_SERVICE_TEMP_UNAVAILABLE] = ERROR_NETWORK; ERROR_TO_GENERAL_ERROR_CLASS[ERROR_PARSE] = ERROR_NETWORK;
--- a/services/fxaccounts/FxAccountsWebChannel.jsm +++ b/services/fxaccounts/FxAccountsWebChannel.jsm @@ -256,19 +256,17 @@ this.FxAccountsWebChannelHelpers.prototy /** * logout the fxaccounts service * * @param the uid of the account which have been logged out */ logout(uid) { return fxAccounts.getSignedInUser().then(userData => { if (userData.uid === uid) { - // true argument is `localOnly`, because server-side stuff - // has already been taken care of by the content server - return fxAccounts.signOut(true); + return fxAccounts.signOut(); } }); }, /** * Get the hash of account name of the previously signed in account */ getPreviousAccountNameHashPref() {
--- a/services/fxaccounts/tests/xpcshell/test_accounts.js +++ b/services/fxaccounts/tests/xpcshell/test_accounts.js @@ -122,18 +122,17 @@ function MockFxAccountsClient() { this.resendVerificationEmail = function(sessionToken) { // Return the session token to show that we received it in the first place return Promise.resolve(sessionToken); }; this.signCertificate = function() { throw "no" }; - this.signOut = () => Promise.resolve(); - this.signOutAndDestroyDevice = () => Promise.resolve({}); + this.signOut = function() { return Promise.resolve(); }; FxAccountsClient.apply(this); } MockFxAccountsClient.prototype = { __proto__: FxAccountsClient.prototype } /* @@ -158,19 +157,16 @@ function MockFxAccounts() { storage.initialize(credentials); return new AccountState(storage); }, getCertificateSigned: function (sessionToken, serializedPublicKey) { _("mock getCertificateSigned\n"); this._getCertificateSigned_calls.push([sessionToken, serializedPublicKey]); return this._d_signCertificate.promise; }, - _registerOrUpdateDevice() { - return Promise.resolve(); - }, fxAccountsClient: new MockFxAccountsClient() }); } /* * Some tests want a "real" fxa instance - however, we still mock the storage * to keep the tests fast on b2g. */ @@ -178,22 +174,16 @@ function MakeFxAccounts(internal = {}) { if (!internal.newAccountState) { // we use a real accountState but mocked storage. internal.newAccountState = function(credentials) { let storage = new MockStorageManager(); storage.initialize(credentials); return new AccountState(storage); }; } - if (!internal._signOutServer) { - internal._signOutServer = () => Promise.resolve(); - } - if (!internal._registerOrUpdateDevice) { - internal._registerOrUpdateDevice = () => Promise.resolve(); - } return new FxAccounts(internal); } add_test(function test_non_https_remote_server_uri_with_requireHttps_false() { Services.prefs.setBoolPref( "identity.fxaccounts.allowHttp", true); Services.prefs.setCharPref( @@ -215,17 +205,17 @@ add_test(function test_non_https_remote_ fxAccounts.getAccountsSignUpURI(); }, "Firefox Accounts server must use HTTPS"); Services.prefs.clearUserPref("identity.fxaccounts.remote.signup.uri"); run_next_test(); }); -add_task(function* test_get_signed_in_user_initially_unset() { +add_task(function test_get_signed_in_user_initially_unset() { _("Check getSignedInUser initially and after signout reports no user"); let account = MakeFxAccounts(); let credentials = { email: "foo@example.com", uid: "1234@lcip.org", assertion: "foobar", sessionToken: "dead", kA: "beef", @@ -560,17 +550,17 @@ add_test(function test_overlapping_signi log.debug("Bob verifying his email ..."); fxa.internal.fxAccountsClient._verified = true; }); }); }); }); }); -add_task(function* test_getAssertion() { +add_task(function test_getAssertion() { let fxa = new MockFxAccounts(); do_check_throws(function() { yield fxa.getAssertion("nonaudience"); }); let creds = { sessionToken: "sessionToken", @@ -680,17 +670,17 @@ add_task(function* test_getAssertion() { do_check_eq(keyPair.validUntil, now + KEY_LIFETIME); do_check_eq(cert.validUntil, now + CERT_LIFETIME); exp = Number(payload.exp); do_check_eq(exp, now + ASSERTION_LIFETIME); _("----- DONE ----\n"); }); -add_task(function* test_resend_email_not_signed_in() { +add_task(function test_resend_email_not_signed_in() { let fxa = new MockFxAccounts(); try { yield fxa.resendVerificationEmail(); } catch(err) { do_check_eq(err.message, "Cannot resend verification email; no signed-in user"); return; @@ -767,108 +757,31 @@ add_test(function test_resend_email() { // Ok abort polling before we go on to the next test fxa.internal.abortExistingFlow(); run_next_test(); }); }); }); }); -add_task(function* test_sign_out_with_device() { - const fxa = new MockFxAccounts(); - - const credentials = getTestUser("alice"); - yield fxa.internal.setSignedInUser(credentials); - - const user = yield fxa.internal.getUserAccountData(); - do_check_true(user); - Object.keys(credentials).forEach(key => do_check_eq(credentials[key], user[key])); - - const spy = { - signOut: { count: 0 }, - signOutAndDeviceDestroy: { count: 0, args: [] } - }; - const client = fxa.internal.fxAccountsClient; - client.signOut = function () { - spy.signOut.count += 1; - return Promise.resolve(); - }; - client.signOutAndDestroyDevice = function () { - spy.signOutAndDeviceDestroy.count += 1; - spy.signOutAndDeviceDestroy.args.push(arguments); - return Promise.resolve(); - }; - - const promise = new Promise(resolve => { - makeObserver(ONLOGOUT_NOTIFICATION, () => { - log.debug("test_sign_out_with_device observed onlogout"); - // user should be undefined after sign out - fxa.internal.getUserAccountData().then(user2 => { - do_check_eq(user2, null); - do_check_eq(spy.signOut.count, 0); - do_check_eq(spy.signOutAndDeviceDestroy.count, 1); - do_check_eq(spy.signOutAndDeviceDestroy.args[0].length, 3); - do_check_eq(spy.signOutAndDeviceDestroy.args[0][0], credentials.sessionToken); - do_check_eq(spy.signOutAndDeviceDestroy.args[0][1], credentials.deviceId); - do_check_true(spy.signOutAndDeviceDestroy.args[0][2]); - do_check_eq(spy.signOutAndDeviceDestroy.args[0][2].service, "sync"); - resolve(); - }); +add_test(function test_sign_out() { + let fxa = new MockFxAccounts(); + let remoteSignOutCalled = false; + let client = fxa.internal.fxAccountsClient; + client.signOut = function() { remoteSignOutCalled = true; return Promise.resolve(); }; + 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(); }); }); - - yield fxa.signOut(); - - yield promise; -}); - -add_task(function* test_sign_out_without_device() { - const fxa = new MockFxAccounts(); - - const credentials = getTestUser("alice"); - delete credentials.deviceId; - yield fxa.internal.setSignedInUser(credentials); - - const user = yield fxa.internal.getUserAccountData(); - - const spy = { - signOut: { count: 0, args: [] }, - signOutAndDeviceDestroy: { count: 0 } - }; - const client = fxa.internal.fxAccountsClient; - client.signOut = function () { - spy.signOut.count += 1; - spy.signOut.args.push(arguments); - return Promise.resolve(); - }; - client.signOutAndDestroyDevice = function () { - spy.signOutAndDeviceDestroy.count += 1; - return Promise.resolve(); - }; - - const promise = new Promise(resolve => { - makeObserver(ONLOGOUT_NOTIFICATION, () => { - log.debug("test_sign_out_without_device observed onlogout"); - // user should be undefined after sign out - fxa.internal.getUserAccountData().then(user2 => { - do_check_eq(user2, null); - do_check_eq(spy.signOut.count, 1); - do_check_eq(spy.signOut.args[0].length, 2); - do_check_eq(spy.signOut.args[0][0], credentials.sessionToken); - do_check_true(spy.signOut.args[0][1]); - do_check_eq(spy.signOut.args[0][1].service, "sync"); - do_check_eq(spy.signOutAndDeviceDestroy.count, 0); - resolve(); - }); - }); - }); - - yield fxa.signOut(); - - yield promise; + fxa.signOut(); }); add_test(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"; }; @@ -1169,20 +1082,17 @@ add_test(function test_getSignedInUserPr alice.verified = true; let mockProfile = { getProfile: function () { return Promise.resolve({ avatar: "image" }); }, tearDown: function() {}, }; - let fxa = new FxAccounts({ - _signOutServer() { return Promise.resolve(); }, - _registerOrUpdateDevice() { return Promise.resolve(); } - }); + let fxa = new FxAccounts({}); fxa.setSignedInUser(alice).then(() => { fxa.internal._profile = mockProfile; fxa.getSignedInUserProfile() .then(result => { do_check_true(!!result); do_check_eq(result.avatar, "image"); run_next_test(); @@ -1265,17 +1175,16 @@ function expandHex(two_hex) { function expandBytes(two_hex) { return CommonUtils.hexToBytes(expandHex(two_hex)); }; function getTestUser(name) { return { email: name + "@example.com", uid: "1ad7f502-4cc7-4ec1-a209-071fd2fae348", - deviceId: name + "'s device id", sessionToken: name + "'s session token", keyFetchToken: name + "'s keyfetch token", unwrapBKey: expandHex("44"), verified: false }; } function makeObserver(aObserveTopic, aObserveFunc) {
deleted file mode 100644 --- a/services/fxaccounts/tests/xpcshell/test_accounts_device_registration.js +++ /dev/null @@ -1,465 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -Cu.import("resource://services-common/utils.js"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/FxAccounts.jsm"); -Cu.import("resource://gre/modules/FxAccountsClient.jsm"); -Cu.import("resource://gre/modules/FxAccountsCommon.js"); -Cu.import("resource://gre/modules/Promise.jsm"); -Cu.import("resource://gre/modules/Log.jsm"); - -initTestLogging("Trace"); - -var log = Log.repository.getLogger("Services.FxAccounts.test"); -log.level = Log.Level.Debug; - -Services.prefs.setCharPref("identity.fxaccounts.loglevel", "Trace"); -Log.repository.getLogger("FirefoxAccounts").level = Log.Level.Trace; - -Services.prefs.setCharPref("identity.fxaccounts.remote.oauth.uri", "https://example.com/v1"); -Services.prefs.setCharPref("identity.fxaccounts.oauth.client_id", "abc123"); -Services.prefs.setCharPref("identity.fxaccounts.remote.profile.uri", "http://example.com/v1"); -Services.prefs.setCharPref("identity.fxaccounts.settings.uri", "http://accounts.example.com/"); - -function MockStorageManager() { -} - -MockStorageManager.prototype = { - initialize(accountData) { - this.accountData = accountData; - }, - - finalize() { - return Promise.resolve(); - }, - - getAccountData() { - return Promise.resolve(this.accountData); - }, - - updateAccountData(updatedFields) { - for (let [name, value] of Iterator(updatedFields)) { - if (value == null) { - delete this.accountData[name]; - } else { - this.accountData[name] = value; - } - } - return Promise.resolve(); - }, - - deleteAccountData() { - this.accountData = null; - return Promise.resolve(); - } -} - -function MockFxAccountsClient(device) { - this._email = "nobody@example.com"; - this._verified = false; - this._deletedOnServer = false; // for testing accountStatus - - // mock calls up to the auth server to determine whether the - // user account has been verified - this.recoveryEmailStatus = function (sessionToken) { - // simulate a call to /recovery_email/status - return Promise.resolve({ - email: this._email, - verified: this._verified - }); - }; - - this.accountStatus = function(uid) { - let deferred = Promise.defer(); - deferred.resolve(!!uid && (!this._deletedOnServer)); - return deferred.promise; - }; - - const { id: deviceId, name: deviceName, type: deviceType, sessionToken } = device; - - this.registerDevice = (st, name, type) => Promise.resolve({ id: deviceId, name }); - this.updateDevice = (st, id, name) => Promise.resolve({ id, name }); - this.signOutAndDestroyDevice = () => Promise.resolve({}); - this.getDeviceList = (st) => - Promise.resolve([ - { id: deviceId, name: deviceName, type: deviceType, isCurrentDevice: st === sessionToken } - ]); - - FxAccountsClient.apply(this); -} -MockFxAccountsClient.prototype = { - __proto__: FxAccountsClient.prototype -} - -function MockFxAccounts(device = {}) { - return new FxAccounts({ - _getDeviceName() { - return device.name || "mock device name"; - }, - fxAccountsClient: new MockFxAccountsClient(device) - }); -} - -add_task(function* test_updateDeviceRegistration_with_new_device() { - const deviceName = "foo"; - const deviceType = "bar"; - - const credentials = getTestUser("baz"); - delete credentials.deviceId; - const fxa = new MockFxAccounts({ name: deviceName }); - yield fxa.internal.setSignedInUser(credentials); - - const spy = { - registerDevice: { count: 0, args: [] }, - updateDevice: { count: 0, args: [] }, - getDeviceList: { count: 0, args: [] } - }; - const client = fxa.internal.fxAccountsClient; - client.registerDevice = function () { - spy.registerDevice.count += 1; - spy.registerDevice.args.push(arguments); - return Promise.resolve({ - id: "newly-generated device id", - createdAt: Date.now(), - name: deviceName, - type: deviceType - }); - }; - client.updateDevice = function () { - spy.updateDevice.count += 1; - spy.updateDevice.args.push(arguments); - return Promise.resolve({}); - }; - client.getDeviceList = function () { - spy.getDeviceList.count += 1; - spy.getDeviceList.args.push(arguments); - return Promise.resolve([]); - }; - - const result = yield fxa.updateDeviceRegistration(); - - do_check_eq(result, "newly-generated device id"); - do_check_eq(spy.updateDevice.count, 0); - do_check_eq(spy.getDeviceList.count, 0); - do_check_eq(spy.registerDevice.count, 1); - do_check_eq(spy.registerDevice.args[0].length, 3); - do_check_eq(spy.registerDevice.args[0][0], credentials.sessionToken); - do_check_eq(spy.registerDevice.args[0][1], deviceName); - do_check_eq(spy.registerDevice.args[0][2], "desktop"); - - const state = fxa.internal.currentAccountState; - const data = yield state.getUserAccountData(); - - do_check_eq(data.deviceId, "newly-generated device id"); - do_check_false(data.isDeviceStale); -}); - -add_task(function* test_updateDeviceRegistration_with_existing_device() { - const deviceName = "phil's device"; - const deviceType = "desktop"; - - const credentials = getTestUser("pb"); - const fxa = new MockFxAccounts({ name: deviceName }); - yield fxa.internal.setSignedInUser(credentials); - - const spy = { - registerDevice: { count: 0, args: [] }, - updateDevice: { count: 0, args: [] }, - getDeviceList: { count: 0, args: [] } - }; - const client = fxa.internal.fxAccountsClient; - client.registerDevice = function () { - spy.registerDevice.count += 1; - spy.registerDevice.args.push(arguments); - return Promise.resolve({}); - }; - client.updateDevice = function () { - spy.updateDevice.count += 1; - spy.updateDevice.args.push(arguments); - return Promise.resolve({ - id: credentials.deviceId, - name: deviceName - }); - }; - client.getDeviceList = function () { - spy.getDeviceList.count += 1; - spy.getDeviceList.args.push(arguments); - return Promise.resolve([]); - }; - const result = yield fxa.updateDeviceRegistration(); - - do_check_eq(result, credentials.deviceId); - do_check_eq(spy.registerDevice.count, 0); - do_check_eq(spy.getDeviceList.count, 0); - do_check_eq(spy.updateDevice.count, 1); - do_check_eq(spy.updateDevice.args[0].length, 3); - do_check_eq(spy.updateDevice.args[0][0], credentials.sessionToken); - do_check_eq(spy.updateDevice.args[0][1], credentials.deviceId); - do_check_eq(spy.updateDevice.args[0][2], deviceName); - - const state = fxa.internal.currentAccountState; - const data = yield state.getUserAccountData(); - - do_check_eq(data.deviceId, credentials.deviceId); - do_check_false(data.isDeviceStale); -}); - -add_task(function* test_updateDeviceRegistration_with_unknown_device_error() { - const deviceName = "foo"; - const deviceType = "bar"; - - const credentials = getTestUser("baz"); - const fxa = new MockFxAccounts({ name: deviceName }); - yield fxa.internal.setSignedInUser(credentials); - - const spy = { - registerDevice: { count: 0, args: [] }, - updateDevice: { count: 0, args: [] }, - getDeviceList: { count: 0, args: [] } - }; - const client = fxa.internal.fxAccountsClient; - client.registerDevice = function () { - spy.registerDevice.count += 1; - spy.registerDevice.args.push(arguments); - return Promise.resolve({ - id: "a different newly-generated device id", - createdAt: Date.now(), - name: deviceName, - type: deviceType - }); - }; - client.updateDevice = function () { - spy.updateDevice.count += 1; - spy.updateDevice.args.push(arguments); - return Promise.reject({ - code: 400, - errno: ERRNO_UNKNOWN_DEVICE - }); - }; - client.getDeviceList = function () { - spy.getDeviceList.count += 1; - spy.getDeviceList.args.push(arguments); - return Promise.resolve([]); - }; - - const result = yield fxa.updateDeviceRegistration(); - - do_check_null(result); - do_check_eq(spy.getDeviceList.count, 0); - do_check_eq(spy.registerDevice.count, 0); - do_check_eq(spy.updateDevice.count, 1); - do_check_eq(spy.updateDevice.args[0].length, 3); - do_check_eq(spy.updateDevice.args[0][0], credentials.sessionToken); - do_check_eq(spy.updateDevice.args[0][1], credentials.deviceId); - do_check_eq(spy.updateDevice.args[0][2], deviceName); - - const state = fxa.internal.currentAccountState; - const data = yield state.getUserAccountData(); - - do_check_null(data.deviceId); - do_check_false(data.isDeviceStale); -}); - -add_task(function* test_updateDeviceRegistration_with_device_session_conflict_error() { - const deviceName = "foo"; - const deviceType = "bar"; - - const credentials = getTestUser("baz"); - const fxa = new MockFxAccounts({ name: deviceName }); - yield fxa.internal.setSignedInUser(credentials); - - const spy = { - registerDevice: { count: 0, args: [] }, - updateDevice: { count: 0, args: [], times: [] }, - getDeviceList: { count: 0, args: [] } - }; - const client = fxa.internal.fxAccountsClient; - client.registerDevice = function () { - spy.registerDevice.count += 1; - spy.registerDevice.args.push(arguments); - return Promise.resolve({}); - }; - client.updateDevice = function () { - spy.updateDevice.count += 1; - spy.updateDevice.args.push(arguments); - spy.updateDevice.time = Date.now(); - if (spy.updateDevice.count === 1) { - return Promise.reject({ - code: 400, - errno: ERRNO_DEVICE_SESSION_CONFLICT - }); - } - return Promise.resolve({ - id: credentials.deviceId, - name: deviceName - }); - }; - client.getDeviceList = function () { - spy.getDeviceList.count += 1; - spy.getDeviceList.args.push(arguments); - spy.getDeviceList.time = Date.now(); - return Promise.resolve([ - { id: "ignore", name: "ignore", type: "ignore", isCurrentDevice: false }, - { id: credentials.deviceId, name: deviceName, type: deviceType, isCurrentDevice: true } - ]); - }; - - const result = yield fxa.updateDeviceRegistration(); - - do_check_eq(result, credentials.deviceId); - do_check_eq(spy.registerDevice.count, 0); - do_check_eq(spy.updateDevice.count, 1); - do_check_eq(spy.updateDevice.args[0].length, 3); - do_check_eq(spy.updateDevice.args[0][0], credentials.sessionToken); - do_check_eq(spy.updateDevice.args[0][1], credentials.deviceId); - do_check_eq(spy.updateDevice.args[0][2], deviceName); - do_check_eq(spy.getDeviceList.count, 1); - do_check_eq(spy.getDeviceList.args[0].length, 1); - do_check_eq(spy.getDeviceList.args[0][0], credentials.sessionToken); - do_check_true(spy.getDeviceList.time >= spy.updateDevice.time); - - const state = fxa.internal.currentAccountState; - const data = yield state.getUserAccountData(); - - do_check_eq(data.deviceId, credentials.deviceId); - do_check_true(data.isDeviceStale); -}); - -add_task(function* test_updateDeviceRegistration_with_unrecoverable_error() { - const deviceName = "foo"; - const deviceType = "bar"; - - const credentials = getTestUser("baz"); - delete credentials.deviceId; - const fxa = new MockFxAccounts({ name: deviceName }); - yield fxa.internal.setSignedInUser(credentials); - - const spy = { - registerDevice: { count: 0, args: [] }, - updateDevice: { count: 0, args: [] }, - getDeviceList: { count: 0, args: [] } - }; - const client = fxa.internal.fxAccountsClient; - client.registerDevice = function () { - spy.registerDevice.count += 1; - spy.registerDevice.args.push(arguments); - return Promise.reject({ - code: 400, - errno: ERRNO_TOO_MANY_CLIENT_REQUESTS - }); - }; - client.updateDevice = function () { - spy.updateDevice.count += 1; - spy.updateDevice.args.push(arguments); - return Promise.resolve({}); - }; - client.getDeviceList = function () { - spy.getDeviceList.count += 1; - spy.getDeviceList.args.push(arguments); - return Promise.resolve([]); - }; - - const result = yield fxa.updateDeviceRegistration(); - - do_check_null(result); - do_check_eq(spy.getDeviceList.count, 0); - do_check_eq(spy.updateDevice.count, 0); - do_check_eq(spy.registerDevice.count, 1); - do_check_eq(spy.registerDevice.args[0].length, 3); - - const state = fxa.internal.currentAccountState; - const data = yield state.getUserAccountData(); - - do_check_null(data.deviceId); -}); - -add_task(function* test_getDeviceId_with_no_device_id_invokes_device_registration() { - const credentials = getTestUser("foo"); - credentials.verified = true; - delete credentials.deviceId; - const fxa = new MockFxAccounts(); - yield fxa.internal.setSignedInUser(credentials); - - const spy = { count: 0, args: [] }; - fxa.internal._registerOrUpdateDevice = function () { - spy.count += 1; - spy.args.push(arguments); - return Promise.resolve("bar"); - }; - - const result = yield fxa.internal.getDeviceId(); - - do_check_eq(spy.count, 1); - do_check_eq(spy.args[0].length, 1); - do_check_eq(spy.args[0][0].email, credentials.email); - do_check_null(spy.args[0][0].deviceId); - do_check_eq(result, "bar"); -}); - -add_task(function* test_getDeviceId_with_device_id_and_stale_flag_invokes_device_registration() { - const credentials = getTestUser("foo"); - credentials.verified = true; - const fxa = new MockFxAccounts(); - yield fxa.internal.setSignedInUser(credentials); - - const spy = { count: 0, args: [] }; - fxa.internal.currentAccountState.getUserAccountData = - () => Promise.resolve({ deviceId: credentials.deviceId, isDeviceStale: true }); - fxa.internal._registerOrUpdateDevice = function () { - spy.count += 1; - spy.args.push(arguments); - return Promise.resolve("wibble"); - }; - - const result = yield fxa.internal.getDeviceId(); - - do_check_eq(spy.count, 1); - do_check_eq(spy.args[0].length, 1); - do_check_eq(spy.args[0][0].deviceId, credentials.deviceId); - do_check_eq(result, "wibble"); -}); - -add_task(function* test_getDeviceId_with_device_id_and_no_stale_flag_doesnt_invoke_device_registration() { - const credentials = getTestUser("foo"); - credentials.verified = true; - const fxa = new MockFxAccounts(); - yield fxa.internal.setSignedInUser(credentials); - - const spy = { count: 0 }; - fxa.internal._registerOrUpdateDevice = function () { - spy.count += 1; - return Promise.resolve("bar"); - }; - - const result = yield fxa.internal.getDeviceId(); - - do_check_eq(spy.count, 0); - do_check_eq(result, "foo's device id"); -}); - -function expandHex(two_hex) { - // Return a 64-character hex string, encoding 32 identical bytes. - let eight_hex = two_hex + two_hex + two_hex + two_hex; - let thirtytwo_hex = eight_hex + eight_hex + eight_hex + eight_hex; - return thirtytwo_hex + thirtytwo_hex; -}; - -function expandBytes(two_hex) { - return CommonUtils.hexToBytes(expandHex(two_hex)); -}; - -function getTestUser(name) { - return { - email: name + "@example.com", - uid: "1ad7f502-4cc7-4ec1-a209-071fd2fae348", - deviceId: name + "'s device id", - sessionToken: name + "'s session token", - keyFetchToken: name + "'s keyfetch token", - unwrapBKey: expandHex("44"), - verified: false - }; -} -
--- a/services/fxaccounts/tests/xpcshell/test_client.js +++ b/services/fxaccounts/tests/xpcshell/test_client.js @@ -35,17 +35,17 @@ var ACCOUNT_KEYS = { }; function deferredStop(server) { let deferred = Promise.defer(); server.stop(deferred.resolve); return deferred.promise; } -add_task(function* test_authenticated_get_request() { +add_task(function test_authenticated_get_request() { let message = "{\"msg\": \"Great Success!\"}"; let credentials = { id: "eyJleHBpcmVzIjogMTM2NTAxMDg5OC4x", key: "qTZf4ZFpAMpMoeSsX3zVRjiqmNs=", algorithm: "sha256" }; let method = "GET"; @@ -60,17 +60,17 @@ add_task(function* test_authenticated_ge let client = new FxAccountsClient(server.baseURI); let result = yield client._request("/foo", method, credentials); do_check_eq("Great Success!", result.msg); yield deferredStop(server); }); -add_task(function* test_authenticated_post_request() { +add_task(function test_authenticated_post_request() { let credentials = { id: "eyJleHBpcmVzIjogMTM2NTAxMDg5OC4x", key: "qTZf4ZFpAMpMoeSsX3zVRjiqmNs=", algorithm: "sha256" }; let method = "POST"; let server = httpd_setup({"/foo": function(request, response) { @@ -85,17 +85,17 @@ add_task(function* test_authenticated_po let client = new FxAccountsClient(server.baseURI); let result = yield client._request("/foo", method, credentials, {foo: "bar"}); do_check_eq("bar", result.foo); yield deferredStop(server); }); -add_task(function* test_500_error() { +add_task(function test_500_error() { let message = "<h1>Ooops!</h1>"; let method = "GET"; let server = httpd_setup({"/foo": function(request, response) { response.setStatusLine(request.httpVersion, 500, "Internal Server Error"); response.bodyOutputStream.write(message, message.length); } }); @@ -108,17 +108,17 @@ add_task(function* test_500_error() { } catch (e) { do_check_eq(500, e.code); do_check_eq("Internal Server Error", e.message); } yield deferredStop(server); }); -add_task(function* test_backoffError() { +add_task(function test_backoffError() { let method = "GET"; let server = httpd_setup({ "/retryDelay": function(request, response) { response.setHeader("Retry-After", "30"); response.setStatusLine(request.httpVersion, 429, "Client has sent too many requests"); let message = "<h1>Ooops!</h1>"; response.bodyOutputStream.write(message, message.length); }, @@ -155,17 +155,17 @@ add_task(function* test_backoffError() { client._clearBackoff(); let result = yield client._request("/duringDelayIShouldNotBeCalled", method); do_check_eq(client.backoffError, null); do_check_eq(result.working, "yes"); yield deferredStop(server); }); -add_task(function* test_signUp() { +add_task(function test_signUp() { let creationMessage_noKey = JSON.stringify({ uid: "uid", sessionToken: "sessionToken" }); let creationMessage_withKey = JSON.stringify({ uid: "uid", sessionToken: "sessionToken", keyFetchToken: "keyFetchToken" @@ -233,17 +233,17 @@ add_task(function* test_signUp() { do_throw("Expected to catch an exception"); } catch(expectedError) { do_check_eq(101, expectedError.errno); } yield deferredStop(server); }); -add_task(function* test_signIn() { +add_task(function test_signIn() { let sessionMessage_noKey = JSON.stringify({ sessionToken: FAKE_SESSION_TOKEN }); let sessionMessage_withKey = JSON.stringify({ sessionToken: FAKE_SESSION_TOKEN, keyFetchToken: "keyFetchToken" }); let errorMessage_notExistent = JSON.stringify({ @@ -324,17 +324,17 @@ add_task(function* test_signIn() { do_throw("Expected to catch an exception"); } catch (expectedError) { do_check_eq(102, expectedError.errno); } yield deferredStop(server); }); -add_task(function* test_signOut() { +add_task(function test_signOut() { let signoutMessage = JSON.stringify({}); let errorMessage = JSON.stringify({code: 400, errno: 102, error: "doesn't exist"}); let signedOut = false; let server = httpd_setup({ "/session/destroy": function(request, response) { if (!signedOut) { signedOut = true; @@ -361,17 +361,17 @@ add_task(function* test_signOut() { do_throw("Expected to catch an exception"); } catch(expectedError) { do_check_eq(102, expectedError.errno); } yield deferredStop(server); }); -add_task(function* test_recoveryEmailStatus() { +add_task(function test_recoveryEmailStatus() { let emailStatus = JSON.stringify({verified: true}); let errorMessage = JSON.stringify({code: 400, errno: 102, error: "doesn't exist"}); let tries = 0; let server = httpd_setup({ "/recovery_email/status": function(request, response) { do_check_true(request.hasHeader("Authorization")); @@ -399,17 +399,17 @@ add_task(function* test_recoveryEmailSta do_throw("Expected to catch an exception"); } catch(expectedError) { do_check_eq(102, expectedError.errno); } yield deferredStop(server); }); -add_task(function* test_resendVerificationEmail() { +add_task(function test_resendVerificationEmail() { let emptyMessage = "{}"; let errorMessage = JSON.stringify({code: 400, errno: 102, error: "doesn't exist"}); let tries = 0; let server = httpd_setup({ "/recovery_email/resend_code": function(request, response) { do_check_true(request.hasHeader("Authorization")); if (tries === 0) { @@ -436,17 +436,17 @@ add_task(function* test_resendVerificati do_throw("Expected to catch an exception"); } catch(expectedError) { do_check_eq(102, expectedError.errno); } yield deferredStop(server); }); -add_task(function* test_accountKeys() { +add_task(function test_accountKeys() { // Four calls to accountKeys(). The first one should work correctly, and we // should get a valid bundle back, in exchange for our keyFetch token, from // which we correctly derive kA and wrapKB. The subsequent three calls // should all trigger separate error paths. let responseMessage = JSON.stringify({bundle: ACCOUNT_KEYS.response}); let errorMessage = JSON.stringify({code: 400, errno: 102, error: "doesn't exist"}); let emptyMessage = "{}"; let attempt = 0; @@ -517,17 +517,17 @@ add_task(function* test_accountKeys() { do_throw("Expected to catch an exception"); } catch(expectedError) { do_check_eq(102, expectedError.errno); } yield deferredStop(server); }); -add_task(function* test_signCertificate() { +add_task(function test_signCertificate() { let certSignMessage = JSON.stringify({cert: {bar: "baz"}}); let errorMessage = JSON.stringify({code: 400, errno: 102, error: "doesn't exist"}); let tries = 0; let server = httpd_setup({ "/certificate/sign": function(request, response) { do_check_true(request.hasHeader("Authorization")); @@ -559,17 +559,17 @@ add_task(function* test_signCertificate( do_throw("Expected to catch an exception"); } catch(expectedError) { do_check_eq(102, expectedError.errno); } yield deferredStop(server); }); -add_task(function* test_accountExists() { +add_task(function test_accountExists() { let sessionMessage = JSON.stringify({sessionToken: FAKE_SESSION_TOKEN}); let existsMessage = JSON.stringify({error: "wrong password", code: 400, errno: 103}); let doesntExistMessage = JSON.stringify({error: "no such account", code: 400, errno: 102}); let emptyMessage = "{}"; let server = httpd_setup({ "/account/login": function(request, response) { let body = CommonUtils.readBytesFromInputStream(request.bodyInputStream); @@ -620,183 +620,16 @@ add_task(function* test_accountExists() do_throw("Expected to catch an exception"); } catch(unexpectedError) { do_check_eq(unexpectedError.code, 500); } yield deferredStop(server); }); -add_task(function* test_registerDevice() { - const DEVICE_ID = "device id"; - const DEVICE_NAME = "device name"; - const DEVICE_TYPE = "device type"; - const ERROR_NAME = "test that the client promise rejects"; - - const server = httpd_setup({ - "/account/device": function(request, response) { - const body = JSON.parse(CommonUtils.readBytesFromInputStream(request.bodyInputStream)); - - if (body.id || !body.name || !body.type || Object.keys(body).length !== 2) { - response.setStatusLine(request.httpVersion, 400, "Invalid request"); - return response.bodyOutputStream.write("{}", 2); - } - - if (body.name === ERROR_NAME) { - response.setStatusLine(request.httpVersion, 500, "Alas"); - return response.bodyOutputStream.write("{}", 2); - } - - body.id = DEVICE_ID; - body.createdAt = Date.now(); - - const responseMessage = JSON.stringify(body); - - response.setStatusLine(request.httpVersion, 200, "OK"); - response.bodyOutputStream.write(responseMessage, responseMessage.length); - }, - }); - - const client = new FxAccountsClient(server.baseURI); - const result = yield client.registerDevice(FAKE_SESSION_TOKEN, DEVICE_NAME, DEVICE_TYPE); - - do_check_true(result); - do_check_eq(Object.keys(result).length, 4); - do_check_eq(result.id, DEVICE_ID); - do_check_eq(typeof result.createdAt, 'number'); - do_check_true(result.createdAt > 0); - do_check_eq(result.name, DEVICE_NAME); - do_check_eq(result.type, DEVICE_TYPE); - - try { - yield client.registerDevice(FAKE_SESSION_TOKEN, ERROR_NAME, DEVICE_TYPE); - do_throw("Expected to catch an exception"); - } catch(unexpectedError) { - do_check_eq(unexpectedError.code, 500); - } - - yield deferredStop(server); -}); - -add_task(function* test_updateDevice() { - const DEVICE_ID = "some other id"; - const DEVICE_NAME = "some other name"; - const ERROR_ID = "test that the client promise rejects"; - - const server = httpd_setup({ - "/account/device": function(request, response) { - const body = JSON.parse(CommonUtils.readBytesFromInputStream(request.bodyInputStream)); - - if (!body.id || !body.name || body.type || Object.keys(body).length !== 2) { - response.setStatusLine(request.httpVersion, 400, "Invalid request"); - return response.bodyOutputStream.write("{}", 2); - } - - if (body.id === ERROR_ID) { - response.setStatusLine(request.httpVersion, 500, "Alas"); - return response.bodyOutputStream.write("{}", 2); - } - - const responseMessage = JSON.stringify(body); - - response.setStatusLine(request.httpVersion, 200, "OK"); - response.bodyOutputStream.write(responseMessage, responseMessage.length); - }, - }); - - const client = new FxAccountsClient(server.baseURI); - const result = yield client.updateDevice(FAKE_SESSION_TOKEN, DEVICE_ID, DEVICE_NAME); - - do_check_true(result); - do_check_eq(Object.keys(result).length, 2); - do_check_eq(result.id, DEVICE_ID); - do_check_eq(result.name, DEVICE_NAME); - - try { - yield client.updateDevice(FAKE_SESSION_TOKEN, ERROR_ID, DEVICE_NAME); - do_throw("Expected to catch an exception"); - } catch(unexpectedError) { - do_check_eq(unexpectedError.code, 500); - } - - yield deferredStop(server); -}); - -add_task(function* test_signOutAndDestroyDevice() { - const DEVICE_ID = "device id"; - const ERROR_ID = "test that the client promise rejects"; - - const server = httpd_setup({ - "/account/device/destroy": function(request, response) { - const body = JSON.parse(CommonUtils.readBytesFromInputStream(request.bodyInputStream)); - - if (!body.id) { - response.setStatusLine(request.httpVersion, 400, "Invalid request"); - return response.bodyOutputStream.write(emptyMessage, emptyMessage.length); - } - - if (body.id === ERROR_ID) { - response.setStatusLine(request.httpVersion, 500, "Alas"); - return response.bodyOutputStream.write("{}", 2); - } - - response.setStatusLine(request.httpVersion, 200, "OK"); - response.bodyOutputStream.write("{}", 2); - }, - }); - - const client = new FxAccountsClient(server.baseURI); - const result = yield client.signOutAndDestroyDevice(FAKE_SESSION_TOKEN, DEVICE_ID); - - do_check_true(result); - do_check_eq(Object.keys(result).length, 0); - - try { - yield client.signOutAndDestroyDevice(FAKE_SESSION_TOKEN, ERROR_ID); - do_throw("Expected to catch an exception"); - } catch(unexpectedError) { - do_check_eq(unexpectedError.code, 500); - } - - yield deferredStop(server); -}); - -add_task(function* test_getDeviceList() { - let canReturnDevices; - - const server = httpd_setup({ - "/account/devices": function(request, response) { - if (canReturnDevices) { - response.setStatusLine(request.httpVersion, 200, "OK"); - response.bodyOutputStream.write("[]", 2); - } else { - response.setStatusLine(request.httpVersion, 500, "Alas"); - response.bodyOutputStream.write("{}", 2); - } - }, - }); - - const client = new FxAccountsClient(server.baseURI); - - canReturnDevices = true; - const result = yield client.getDeviceList(FAKE_SESSION_TOKEN); - do_check_true(Array.isArray(result)); - do_check_eq(result.length, 0); - - try { - canReturnDevices = false; - yield client.getDeviceList(FAKE_SESSION_TOKEN); - do_throw("Expected to catch an exception"); - } catch(unexpectedError) { - do_check_eq(unexpectedError.code, 500); - } - - yield deferredStop(server); -}); - add_task(function* test_client_metrics() { ["FXA_UNVERIFIED_ACCOUNT_ERRORS", "FXA_HAWK_ERRORS", "FXA_SERVER_ERRORS"].forEach(name => { let histogram = Services.telemetry.getKeyedHistogramById(name); histogram.clear(); }); function writeResp(response, msg) { if (typeof msg === "object") { @@ -872,17 +705,17 @@ add_task(function* test_client_metrics() return err.code == 504; }); assertSnapshot("FXA_SERVER_ERRORS", "/recovery_email/resend_code", "Should report 500-class errors"); yield deferredStop(server); }); -add_task(function* test_email_case() { +add_task(function test_email_case() { let canonicalEmail = "greta.garbo@gmail.com"; let clientEmail = "Greta.Garbo@gmail.COM"; let attempts = 0; function writeResp(response, msg) { if (typeof msg === "object") { msg = JSON.stringify(msg); }
--- a/services/fxaccounts/tests/xpcshell/test_credentials.js +++ b/services/fxaccounts/tests/xpcshell/test_credentials.js @@ -24,17 +24,17 @@ var vectors = { authPW: h("247b675ffb4c4631 0bc87e26d712153a be5e1c90ef00a478 4594f97ef54f2375"), authSalt: h("00f0000000000000 0000000000000000 0000000000000000 0000000000000000"), }, }; // A simple test suite with no utf8 encoding madness. -add_task(function* test_onepw_setup_credentials() { +add_task(function test_onepw_setup_credentials() { let email = "francine@example.org"; let password = CommonUtils.encodeUTF8("i like pie"); let pbkdf2 = CryptoUtils.pbkdf2Generate; let hkdf = CryptoUtils.hkdf; // quickStretch the email let saltyEmail = Credentials.keyWordExtended("quickStretch", email); @@ -61,17 +61,17 @@ add_task(function* test_onepw_setup_cred // derive unwrap key let unwrapKeyInfo = Credentials.keyWord('unwrapBkey'); let unwrapKey = hkdf(quickStretchedPW, hkdfSalt, unwrapKeyInfo, hkdfLen); do_check_eq(b2h(unwrapKey), "8ff58975be391338e4ec5d7138b5ed7b65c7d1bfd1f3a4f93e05aa47d5b72be9"); }); -add_task(function* test_client_stretch_kdf() { +add_task(function test_client_stretch_kdf() { let pbkdf2 = CryptoUtils.pbkdf2Generate; let hkdf = CryptoUtils.hkdf; let expected = vectors["client stretch-KDF"]; let emailUTF8 = h2s(expected.email); let passwordUTF8 = h2s(expected.password); // Intermediate value from sjcl implementation in fxa-js-client
--- a/services/fxaccounts/tests/xpcshell/test_loginmgr_storage.js +++ b/services/fxaccounts/tests/xpcshell/test_loginmgr_storage.js @@ -39,26 +39,18 @@ function getLoginMgrData() { let logins = Services.logins.findLogins({}, FXA_PWDMGR_HOST, null, FXA_PWDMGR_REALM); if (logins.length == 0) { return null; } Assert.equal(logins.length, 1, "only 1 login available"); return logins[0]; } -function createFxAccounts() { - return new FxAccounts({ - _getDeviceName() { - return "mock device name"; - } - }); -} - -add_task(function* test_simple() { - let fxa = createFxAccounts(); +add_task(function test_simple() { + let fxa = new FxAccounts({}); let creds = { uid: "abcd", email: "test@example.com", sessionToken: "sessionToken", kA: "the kA value", kB: "the kB value", verified: true @@ -87,18 +79,18 @@ add_task(function* test_simple() { Assert.ok(!("email" in loginData), "email not stored in the login mgr json"); Assert.ok(!("sessionToken" in loginData), "sessionToken not stored in the login mgr json"); Assert.ok(!("verified" in loginData), "verified not stored in the login mgr json"); yield fxa.signOut(/* localOnly = */ true); Assert.strictEqual(getLoginMgrData(), null, "login mgr data deleted on logout"); }); -add_task(function* test_MPLocked() { - let fxa = createFxAccounts(); +add_task(function test_MPLocked() { + let fxa = new FxAccounts({}); let creds = { uid: "abcd", email: "test@example.com", sessionToken: "sessionToken", kA: "the kA value", kB: "the kB value", verified: true @@ -121,20 +113,20 @@ add_task(function* test_MPLocked() { Assert.ok(!("kA" in data.accountData), "kA not stored in clear text"); Assert.ok(!("kB" in data.accountData), "kB not stored in clear text"); Assert.strictEqual(getLoginMgrData(), null, "login mgr data doesn't exist"); yield fxa.signOut(/* localOnly = */ true) }); -add_task(function* test_consistentWithMPEdgeCases() { +add_task(function test_consistentWithMPEdgeCases() { setLoginMgrLoggedInState(true); - let fxa = createFxAccounts(); + let fxa = new FxAccounts({}); let creds1 = { uid: "uid1", email: "test@example.com", sessionToken: "sessionToken", kA: "the kA value", kB: "the kB value", verified: true @@ -164,28 +156,28 @@ add_task(function* test_consistentWithMP Assert.strictEqual(login.username, creds1.email); // and that we do have the first kA in the login manager. Assert.strictEqual(JSON.parse(login.password).accountData.kA, creds1.kA, "stale data still in login mgr"); // Make a new FxA instance (otherwise the values in memory will be used) // and we want the login manager to be unlocked. setLoginMgrLoggedInState(true); - fxa = createFxAccounts(); + fxa = new FxAccounts({}); let accountData = yield fxa.getSignedInUser(); Assert.strictEqual(accountData.email, creds2.email); // we should have no kA at all. Assert.strictEqual(accountData.kA, undefined, "stale kA wasn't used"); yield fxa.signOut(/* localOnly = */ true) }); // A test for the fact we will accept either a UID or email when looking in // the login manager. -add_task(function* test_uidMigration() { +add_task(function test_uidMigration() { setLoginMgrLoggedInState(true); Assert.strictEqual(getLoginMgrData(), null, "expect no logins at the start"); // create the login entry using uid as a key. let contents = {kA: "kA"}; let loginInfo = new Components.Constructor( "@mozilla.org/login-manager/loginInfo;1", Ci.nsILoginInfo, "init");
--- a/services/fxaccounts/tests/xpcshell/test_oauth_grant_client.js +++ b/services/fxaccounts/tests/xpcshell/test_oauth_grant_client.js @@ -1,16 +1,15 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; Cu.import("resource://gre/modules/FxAccountsCommon.js"); Cu.import("resource://gre/modules/FxAccountsOAuthGrantClient.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); const CLIENT_OPTIONS = { serverURL: "http://127.0.0.1:9010/v1", client_id: 'abc123' }; const STATUS_SUCCESS = 200; @@ -141,29 +140,27 @@ add_test(function serverErrorResponse () ); }); add_test(function networkErrorResponse () { let client = new FxAccountsOAuthGrantClient({ serverURL: "http://", client_id: "abc123" }); - Services.prefs.setBoolPref("identity.fxaccounts.skipDeviceRegistration", true); client.getTokenFromAssertion("assertion", "scope") .then( null, function (e) { do_check_eq(e.name, "FxAccountsOAuthGrantClientError"); do_check_eq(e.code, null); do_check_eq(e.errno, ERRNO_NETWORK); do_check_eq(e.error, ERROR_NETWORK); run_next_test(); } - ).catch(() => {}).then(() => - Services.prefs.clearUserPref("identity.fxaccounts.skipDeviceRegistration")); + ); }); add_test(function unsupportedMethod () { let client = new FxAccountsOAuthGrantClient(CLIENT_OPTIONS); return client._createRequest("/", "PUT") .then( null,
--- a/services/fxaccounts/tests/xpcshell/test_oauth_grant_client_server.js +++ b/services/fxaccounts/tests/xpcshell/test_oauth_grant_client_server.js @@ -45,17 +45,17 @@ function startServer() { } function promiseStopServer(server) { return new Promise(resolve => { server.stop(resolve); }); } -add_task(function* getAndRevokeToken () { +add_task(function getAndRevokeToken () { let server = startServer(); let clientOptions = { serverURL: "http://localhost:" + server.identity.primaryPort + "/v1", client_id: 'abc123', } let client = new FxAccountsOAuthGrantClient(clientOptions); let result = yield client.getTokenFromAssertion("assertion", "scope");
--- a/services/fxaccounts/tests/xpcshell/test_oauth_token_storage.js +++ b/services/fxaccounts/tests/xpcshell/test_oauth_token_storage.js @@ -65,40 +65,33 @@ function MockFxAccountsClient() { this.accountStatus = function(uid) { let deferred = Promise.defer(); deferred.resolve(!!uid && (!this._deletedOnServer)); return deferred.promise; }; this.signOut = function() { return Promise.resolve(); }; - this.registerDevice = function() { return Promise.resolve(); }; - this.updateDevice = function() { return Promise.resolve(); }; - this.signOutAndDestroyDevice = function() { return Promise.resolve(); }; - this.getDeviceList = function() { return Promise.resolve(); }; FxAccountsClient.apply(this); } MockFxAccountsClient.prototype = { __proto__: FxAccountsClient.prototype } -function MockFxAccounts(device={}) { +function MockFxAccounts() { return new FxAccounts({ fxAccountsClient: new MockFxAccountsClient(), newAccountState(credentials) { // we use a real accountState but mocked storage. let storage = new MockStorageManager(); storage.initialize(credentials); return new AccountState(storage); }, - _getDeviceName() { - return "mock device name"; - } }); } function* createMockFxA() { let fxa = new MockFxAccounts(); let credentials = { email: "foo@example.com", uid: "1234@lcip.org", @@ -112,17 +105,17 @@ function* createMockFxA() { return fxa; } // The tests. function run_test() { run_next_test(); } -add_task(function* testCacheStorage() { +add_task(function testCacheStorage() { let fxa = yield createMockFxA(); // Hook what the impl calls to save to disk. let cas = fxa.internal.currentAccountState; let origPersistCached = cas._persistCachedTokens.bind(cas) cas._persistCachedTokens = function() { return origPersistCached().then(() => { Services.obs.notifyObservers(null, "testhelper-fxa-cache-persist-done", null);
--- a/services/fxaccounts/tests/xpcshell/test_oauth_tokens.js +++ b/services/fxaccounts/tests/xpcshell/test_oauth_tokens.js @@ -62,20 +62,16 @@ function MockFxAccountsClient() { this.accountStatus = function(uid) { let deferred = Promise.defer(); deferred.resolve(!!uid && (!this._deletedOnServer)); return deferred.promise; }; this.signOut = function() { return Promise.resolve(); }; - this.registerDevice = function() { return Promise.resolve(); }; - this.updateDevice = function() { return Promise.resolve(); }; - this.signOutAndDestroyDevice = function() { return Promise.resolve(); }; - this.getDeviceList = function() { return Promise.resolve(); }; FxAccountsClient.apply(this); } MockFxAccountsClient.prototype = { __proto__: FxAccountsClient.prototype } @@ -90,19 +86,16 @@ function MockFxAccounts(mockGrantClient) return new AccountState(storage); }, _destroyOAuthToken: function(tokenData) { // somewhat sad duplication of _destroyOAuthToken, but hard to avoid. return mockGrantClient.destroyToken(tokenData.token).then( () => { Services.obs.notifyObservers(null, "testhelper-fxa-revoke-complete", null); }); }, - _getDeviceName() { - return "mock device name"; - } }); } function* createMockFxA(mockGrantClient) { let fxa = new MockFxAccounts(mockGrantClient); let credentials = { email: "foo@example.com", uid: "1234@lcip.org", @@ -140,17 +133,17 @@ MockFxAccountsOAuthGrantClient.prototype print("after destroy have", this.activeTokens.size, "tokens left."); return Promise.resolve({}); }, // and some stuff used only for tests. numTokenFetches: 0, activeTokens: null, } -add_task(function* testRevoke() { +add_task(function testRevoke() { let client = new MockFxAccountsOAuthGrantClient(); let tokenOptions = { scope: "test-scope", client: client }; let fxa = yield createMockFxA(client); // get our first token and check we hit the mock. let token1 = yield fxa.getOAuthToken(tokenOptions); equal(client.numTokenFetches, 1); equal(client.activeTokens.size, 1); @@ -167,17 +160,17 @@ add_task(function* testRevoke() { // fetching it again hits the server. let token2 = yield fxa.getOAuthToken(tokenOptions); equal(client.numTokenFetches, 2); equal(client.activeTokens.size, 1); ok(token2, "got a token"); notEqual(token1, token2, "got a different token"); }); -add_task(function* testSignOutDestroysTokens() { +add_task(function testSignOutDestroysTokens() { let client = new MockFxAccountsOAuthGrantClient(); let fxa = yield createMockFxA(client); // get our first token and check we hit the mock. let token1 = yield fxa.getOAuthToken({ scope: "test-scope", client: client }); equal(client.numTokenFetches, 1); equal(client.activeTokens.size, 1); ok(token1, "got a token"); @@ -192,17 +185,17 @@ add_task(function* testSignOutDestroysTo // now sign out - they should be removed. yield fxa.signOut(); // FxA fires an observer when the "background" signout is complete. yield promiseNotification("testhelper-fxa-signout-complete"); // No active tokens left. equal(client.activeTokens.size, 0); }); -add_task(function* testTokenRaces() { +add_task(function testTokenRaces() { // Here we do 2 concurrent fetches each for 2 different token scopes (ie, // 4 token fetches in total). // This should provoke a potential race in the token fetching but we should // handle and detect that leaving us with one of the fetch tokens being // revoked and the same token value returned to both calls. let client = new MockFxAccountsOAuthGrantClient(); let fxa = yield createMockFxA(client);
--- a/services/fxaccounts/tests/xpcshell/test_profile.js +++ b/services/fxaccounts/tests/xpcshell/test_profile.js @@ -163,17 +163,17 @@ add_test(function fetchAndCacheProfile_o .then(result => { do_check_eq(result.avatar, "myimg"); run_next_test(); }); }); // Check that a second profile request when one is already in-flight reuses // the in-flight one. -add_task(function* fetchAndCacheProfileOnce() { +add_task(function fetchAndCacheProfileOnce() { // A promise that remains unresolved while we fire off 2 requests for // a profile. let resolveProfile; let promiseProfile = new Promise(resolve => { resolveProfile = resolve; }); let numFetches = 0; let client = mockClient(mockFxa()); @@ -200,17 +200,17 @@ add_task(function* fetchAndCacheProfileO do_check_eq(got2.avatar, "myimg"); // and still only 1 request was made. do_check_eq(numFetches, 1); }); // Check that sharing a single fetch promise works correctly when the promise // is rejected. -add_task(function* fetchAndCacheProfileOnce() { +add_task(function fetchAndCacheProfileOnce() { // A promise that remains unresolved while we fire off 2 requests for // a profile. let rejectProfile; let promiseProfile = new Promise((resolve,reject) => { rejectProfile = reject; }); let numFetches = 0; let client = mockClient(mockFxa()); @@ -246,17 +246,17 @@ add_task(function* fetchAndCacheProfileO }; let got = yield profile._fetchAndCacheProfile(); do_check_eq(got.avatar, "myimg"); }); // Check that a new profile request within PROFILE_FRESHNESS_THRESHOLD of the // last one doesn't kick off a new request to check the cached copy is fresh. -add_task(function* fetchAndCacheProfileAfterThreshold() { +add_task(function fetchAndCacheProfileAfterThreshold() { let numFetches = 0; let client = mockClient(mockFxa()); client.fetchProfile = function () { numFetches += 1; return Promise.resolve({ avatar: "myimg"}); }; let profile = CreateFxAccountsProfile(null, client); profile.PROFILE_FRESHNESS_THRESHOLD = 1000; @@ -273,17 +273,17 @@ add_task(function* fetchAndCacheProfileA yield profile.getProfile(); do_check_eq(numFetches, 2); }); // Check that a new profile request within PROFILE_FRESHNESS_THRESHOLD of the // last one *does* kick off a new request if ON_PROFILE_CHANGE_NOTIFICATION // is sent. -add_task(function* fetchAndCacheProfileBeforeThresholdOnNotification() { +add_task(function fetchAndCacheProfileBeforeThresholdOnNotification() { let numFetches = 0; let client = mockClient(mockFxa()); client.fetchProfile = function () { numFetches += 1; return Promise.resolve({ avatar: "myimg"}); }; let profile = CreateFxAccountsProfile(null, client); profile.PROFILE_FRESHNESS_THRESHOLD = 1000;
--- a/services/fxaccounts/tests/xpcshell/test_storage_manager.js +++ b/services/fxaccounts/tests/xpcshell/test_storage_manager.js @@ -92,78 +92,59 @@ add_storage_task(function* checkInitiali // Initialized with account data (ie, simulating a new user being logged in). // Should reflect the initial data and be written to storage. add_storage_task(function* checkNewUser(sm) { let initialAccountData = { uid: "uid", email: "someone@somewhere.com", kA: "kA", - deviceId: "device id" }; sm.plainStorage = new MockedPlainStorage() if (sm.secureStorage) { sm.secureStorage = new MockedSecureStorage(null); } yield sm.initialize(initialAccountData); let accountData = yield sm.getAccountData(); Assert.equal(accountData.uid, initialAccountData.uid); Assert.equal(accountData.email, initialAccountData.email); Assert.equal(accountData.kA, initialAccountData.kA); - Assert.equal(accountData.deviceId, initialAccountData.deviceId); // and it should have been written to storage. Assert.equal(sm.plainStorage.data.accountData.uid, initialAccountData.uid); Assert.equal(sm.plainStorage.data.accountData.email, initialAccountData.email); - Assert.equal(sm.plainStorage.data.accountData.deviceId, initialAccountData.deviceId); // check secure if (sm.secureStorage) { Assert.equal(sm.secureStorage.data.accountData.kA, initialAccountData.kA); } else { Assert.equal(sm.plainStorage.data.accountData.kA, initialAccountData.kA); } }); // Initialized without account data but storage has it available. add_storage_task(function* checkEverythingRead(sm) { - sm.plainStorage = new MockedPlainStorage({ - uid: "uid", - email: "someone@somewhere.com", - deviceId: "wibble", - isDeviceStale: true - }); + sm.plainStorage = new MockedPlainStorage({uid: "uid", email: "someone@somewhere.com"}) if (sm.secureStorage) { sm.secureStorage = new MockedSecureStorage(null); } yield sm.initialize(); let accountData = yield sm.getAccountData(); Assert.ok(accountData, "read account data"); Assert.equal(accountData.uid, "uid"); Assert.equal(accountData.email, "someone@somewhere.com"); - Assert.equal(accountData.deviceId, "wibble"); - Assert.equal(accountData.isDeviceStale, true); // Update the data - we should be able to fetch it back and it should appear // in our storage. - yield sm.updateAccountData({ - verified: true, - kA: "kA", - kB: "kB", - isDeviceStale: false - }); + yield sm.updateAccountData({verified: true, kA: "kA", kB: "kB"}); accountData = yield sm.getAccountData(); Assert.equal(accountData.kB, "kB"); Assert.equal(accountData.kA, "kA"); - Assert.equal(accountData.deviceId, "wibble"); - Assert.equal(accountData.isDeviceStale, false); // Check the new value was written to storage. yield sm._promiseStorageComplete; // storage is written in the background. - // "verified", "deviceId" and "isDeviceStale" are plain-text fields. + // "verified" is a plain-text field. Assert.equal(sm.plainStorage.data.accountData.verified, true); - Assert.equal(sm.plainStorage.data.accountData.deviceId, "wibble"); - Assert.equal(sm.plainStorage.data.accountData.isDeviceStale, false); // "kA" and "foo" are secure if (sm.secureStorage) { Assert.equal(sm.secureStorage.data.accountData.kA, "kA"); Assert.equal(sm.secureStorage.data.accountData.kB, "kB"); } else { Assert.equal(sm.plainStorage.data.accountData.kA, "kA"); Assert.equal(sm.plainStorage.data.accountData.kB, "kB"); }
--- a/services/fxaccounts/tests/xpcshell/xpcshell.ini +++ b/services/fxaccounts/tests/xpcshell/xpcshell.ini @@ -1,16 +1,14 @@ [DEFAULT] head = head.js ../../../common/tests/unit/head_helpers.js ../../../common/tests/unit/head_http.js tail = skip-if = toolkit == 'android' [test_accounts.js] -[test_accounts_device_registration.js] -skip-if = appname == 'b2g' [test_client.js] skip-if = toolkit == 'gonk' # times out, bug 1073639 [test_credentials.js] [test_loginmgr_storage.js] skip-if = appname == 'b2g' # login manager storage only used on desktop. [test_manager.js] skip-if = appname != 'b2g' reason = FxAccountsManager is only available for B2G for now
--- a/services/sync/modules/constants.js +++ b/services/sync/modules/constants.js @@ -176,12 +176,9 @@ FENNEC_ID: " SEAMONKEY_ID: "{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}", TEST_HARNESS_ID: "xuth@mozilla.org", MIN_PP_LENGTH: 12, MIN_PASS_LENGTH: 8, LOG_DATE_FORMAT: "%Y-%m-%d %H:%M:%S", -DEVICE_TYPE_DESKTOP: "desktop", -DEVICE_TYPE_MOBILE: "mobile", - }))];
--- a/services/sync/modules/engines/clients.js +++ b/services/sync/modules/engines/clients.js @@ -9,20 +9,16 @@ this.EXPORTED_SYMBOLS = [ var {classes: Cc, interfaces: Ci, utils: Cu} = Components; Cu.import("resource://services-common/stringbundle.js"); Cu.import("resource://services-sync/constants.js"); Cu.import("resource://services-sync/engines.js"); Cu.import("resource://services-sync/record.js"); Cu.import("resource://services-sync/util.js"); -Cu.import("resource://services-common/async.js"); - -XPCOMUtils.defineLazyModuleGetter(this, "fxAccounts", - "resource://gre/modules/FxAccounts.jsm"); const CLIENTS_TTL = 1814400; // 21 days const CLIENTS_TTL_REFRESH = 604800; // 7 days const SUPPORTED_PROTOCOL_VERSIONS = ["1.1", "1.5"]; this.ClientsRec = function ClientsRec(collection, id) { CryptoWrapper.call(this, collection, id); @@ -62,24 +58,24 @@ ClientEngine.prototype = { }, set lastRecordUpload(value) { Svc.Prefs.set(this.name + ".lastRecordUpload", Math.floor(value)); }, // Aggregate some stats on the composition of clients on this account get stats() { let stats = { - hasMobile: this.localType == DEVICE_TYPE_MOBILE, + hasMobile: this.localType == "mobile", names: [this.localName], numClients: 1, }; for (let id in this._store._remoteClients) { let {name, type} = this._store._remoteClients[id]; - stats.hasMobile = stats.hasMobile || type == DEVICE_TYPE_MOBILE; + stats.hasMobile = stats.hasMobile || type == "mobile"; stats.names.push(name); stats.numClients++; } return stats; }, /** @@ -115,33 +111,36 @@ ClientEngine.prototype = { }, get brandName() { let brand = new StringBundle("chrome://branding/locale/brand.properties"); return brand.get("brandShortName"); }, get localName() { - return this.localName = Utils.getDeviceName(); + let localName = Svc.Prefs.get("client.name", ""); + if (localName != "") + return localName; + + return this.localName = Utils.getDefaultDeviceName(); }, set localName(value) { Svc.Prefs.set("client.name", value); - fxAccounts.updateDeviceRegistration(); }, get localType() { - return Utils.getDeviceType(); + return Svc.Prefs.get("client.type", "desktop"); }, set localType(value) { Svc.Prefs.set("client.type", value); }, isMobile: function isMobile(id) { if (this._store._remoteClients[id]) - return this._store._remoteClients[id].type == DEVICE_TYPE_MOBILE; + return this._store._remoteClients[id].type == "mobile"; return false; }, _syncStartup: function _syncStartup() { // Reupload new client record periodically. if (Date.now() / 1000 - this.lastRecordUpload > CLIENTS_TTL_REFRESH) { this._tracker.addChangedID(this.localID); this.lastRecordUpload = Date.now() / 1000; @@ -409,23 +408,16 @@ ClientStore.prototype = { this._remoteClients[record.id] = record.cleartext; }, createRecord: function createRecord(id, collection) { let record = new ClientsRec(collection, id); // Package the individual components into a record for the local client if (id == this.engine.localID) { - let cb = Async.makeSpinningCallback(); - fxAccounts.getDeviceId().then(id => cb(null, id), cb); - try { - record.fxaDeviceId = cb.wait(); - } catch(error) { - this._log.warn("failed to get fxa device id", error); - } record.name = this.engine.localName; record.type = this.engine.localType; record.commands = this.engine.localCommands; record.version = Services.appinfo.version; record.protocols = SUPPORTED_PROTOCOL_VERSIONS; // Optional fields. record.os = Services.appinfo.OS; // "Darwin"
--- a/services/sync/modules/util.js +++ b/services/sync/modules/util.js @@ -667,30 +667,16 @@ this.Utils = { // 'device' is defined on unix systems Cc["@mozilla.org/system-info;1"].getService(Ci.nsIPropertyBag2).get("device") || // hostname of the system, usually assigned by the user or admin Cc["@mozilla.org/system-info;1"].getService(Ci.nsIPropertyBag2).get("host") || // fall back on ua info string Cc["@mozilla.org/network/protocol;1?name=http"].getService(Ci.nsIHttpProtocolHandler).oscpu; return Str.sync.get("client.name2", [user, appName, system]); - }, - - getDeviceName() { - const deviceName = Svc.Prefs.get("client.name", ""); - - if (deviceName === "") { - return this.getDefaultDeviceName(); - } - - return deviceName; - }, - - getDeviceType() { - return Svc.Prefs.get("client.type", DEVICE_TYPE_DESKTOP); } }; XPCOMUtils.defineLazyGetter(Utils, "_utf8Converter", function() { let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"] .createInstance(Ci.nsIScriptableUnicodeConverter); converter.charset = "UTF-8"; return converter;
--- a/testing/profiles/prefs_b2g_unittest.js +++ b/testing/profiles/prefs_b2g_unittest.js @@ -3,10 +3,9 @@ user_pref("b2g.system_manifest_url","app://test-container.gaiamobile.org/manifest.webapp"); user_pref("b2g.system_startup_url","app://test-container.gaiamobile.org/index.html"); user_pref("dom.ipc.browser_frames.oop_by_default", false); user_pref("dom.ipc.tabs.disabled", false); user_pref("dom.ipc.tabs.shutdownTimeoutSecs", 0); user_pref("dom.mozBrowserFramesEnabled", "%(OOP)s"); user_pref("dom.mozBrowserFramesWhitelist","app://test-container.gaiamobile.org,http://mochi.test:8888"); user_pref("dom.testing.datastore_enabled_for_hosted_apps", true); -user_pref('identity.fxaccounts.skipDeviceRegistration', true); user_pref("marionette.force-local", true);
--- a/testing/profiles/prefs_general.js +++ b/testing/profiles/prefs_general.js @@ -243,19 +243,16 @@ user_pref('identity.fxaccounts.auth.uri' // Ditto for all the other Firefox accounts URIs used for about:accounts et al.: user_pref("identity.fxaccounts.remote.signup.uri", "https://%(server)s/fxa-signup"); user_pref("identity.fxaccounts.remote.force_auth.uri", "https://%(server)s/fxa-force-auth"); user_pref("identity.fxaccounts.remote.signin.uri", "https://%(server)s/fxa-signin"); user_pref("identity.fxaccounts.settings.uri", "https://%(server)s/fxa-settings"); user_pref('identity.fxaccounts.remote.webchannel.uri', 'https://%(server)s/'); -// We don't want browser tests to perform FxA device registration. -user_pref('identity.fxaccounts.skipDeviceRegistration', true); - // Increase the APZ content response timeout in tests to 1 minute. // This is to accommodate the fact that test environments tends to be slower // than production environments (with the b2g emulator being the slowest of them // all), resulting in the production timeout value sometimes being exceeded // and causing false-positive test failures. See bug 1176798, bug 1177018, // bug 1210465. user_pref("apz.content_response_timeout", 60000);