commit 3f71fb0e98cbefe0f605e9716b85f59fce6cdeaf
parent feb0f48d9b67493e82c43641c22b77b6f2b2dc0a
Author: Mark Hammond <mhammond@skippinet.com.au>
Date: Tue, 16 Dec 2025 05:34:29 +0000
Bug 1928073 - Use the account uid for all FxA merge warnings. r=skhamis,sync-reviewers
Differential Revision: https://phabricator.services.mozilla.com/D270858
Diffstat:
9 files changed, 218 insertions(+), 134 deletions(-)
diff --git a/browser/base/content/test/sync/browser_fxa_web_channel.html b/browser/base/content/test/sync/browser_fxa_web_channel.html
@@ -93,6 +93,7 @@
message: {
command: "fxaccounts:can_link_account",
data: {
+ uid: "uid",
email: "testuser@testuser.com",
},
messageId: 2,
diff --git a/browser/base/content/test/sync/browser_fxa_web_channel.js b/browser/base/content/test/sync/browser_fxa_web_channel.js
@@ -117,13 +117,23 @@ var gTests = [
content_uri: TEST_HTTP_PATH,
channel_id: TEST_CHANNEL_ID,
helpers: {
- shouldAllowRelink(acctName) {
- return acctName === "testuser@testuser.com";
+ _selectableProfilesEnabled() {
+ return false;
},
- promptProfileSyncWarningIfNeeded(acctName) {
- if (acctName === "testuser@testuser.com") {
+ shouldAllowRelink(acctData) {
+ if (acctData.uid == "uid") {
+ Assert.equal(acctData.email, "testuser@testuser.com");
+ return true;
+ }
+ Assert.notEqual(acctData.email, "testuser@testuser.com");
+ return false;
+ },
+ promptProfileSyncWarningIfNeeded(acctData) {
+ if (acctData.uid == "uid") {
+ Assert.equal(acctData.email, "testuser@testuser.com");
return { action: "continue" };
}
+ Assert.notEqual(acctData.email, "testuser@testuser.com");
return { action: "cancel" };
},
},
diff --git a/services/fxaccounts/FxAccounts.sys.mjs b/services/fxaccounts/FxAccounts.sys.mjs
@@ -2,7 +2,6 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-import { CryptoUtils } from "moz-src:///services/crypto/modules/utils.sys.mjs";
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
import { FxAccountsStorageManager } from "resource://gre/modules/FxAccountsStorage.sys.mjs";
@@ -28,7 +27,8 @@ import {
ON_DEVICE_DISCONNECTED_NOTIFICATION,
POLL_SESSION,
PREF_ACCOUNT_ROOT,
- PREF_LAST_FXA_USER,
+ PREF_LAST_FXA_USER_EMAIL,
+ PREF_LAST_FXA_USER_UID,
SERVER_ERRNO_TO_ERROR,
log,
logPII,
@@ -38,6 +38,7 @@ import {
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
+ CryptoUtils: "moz-src:///services/crypto/modules/utils.sys.mjs",
FxAccountsClient: "resource://gre/modules/FxAccountsClient.sys.mjs",
FxAccountsCommands: "resource://gre/modules/FxAccountsCommands.sys.mjs",
FxAccountsConfig: "resource://gre/modules/FxAccountsConfig.sys.mjs",
@@ -1111,6 +1112,19 @@ FxAccountsInternal.prototype = {
return Promise.all(promises);
},
+ // We need to do a one-off migration of a preference to protect against
+ // accidentally merging sync data.
+ // We replace a previously hashed email with a hashed uid.
+ _migratePreviousAccountNameHashPref(uid) {
+ if (Services.prefs.prefHasUserValue(PREF_LAST_FXA_USER_EMAIL)) {
+ Services.prefs.setStringPref(
+ PREF_LAST_FXA_USER_UID,
+ lazy.CryptoUtils.sha256Base64(uid)
+ );
+ Services.prefs.clearUserPref(PREF_LAST_FXA_USER_EMAIL);
+ }
+ },
+
async signOut(localOnly) {
let sessionToken;
let tokensToRevoke;
@@ -1119,6 +1133,7 @@ FxAccountsInternal.prototype = {
if (data) {
sessionToken = data.sessionToken;
tokensToRevoke = data.oauthTokens;
+ this._migratePreviousAccountNameHashPref(data.uid);
}
await this.notifyObservers(ON_PRELOGOUT_NOTIFICATION);
await this._signOutLocal();
@@ -1361,15 +1376,7 @@ FxAccountsInternal.prototype = {
await this.notifyObservers(ON_DEVICE_DISCONNECTED_NOTIFICATION, data);
},
- _setLastUserPref(newEmail) {
- Services.prefs.setStringPref(
- PREF_LAST_FXA_USER,
- CryptoUtils.sha256Base64(newEmail)
- );
- },
-
async _handleEmailUpdated(newEmail) {
- this._setLastUserPref(newEmail);
await this.currentAccountState.updateUserAccountData({ email: newEmail });
},
diff --git a/services/fxaccounts/FxAccountsCommon.sys.mjs b/services/fxaccounts/FxAccountsCommon.sys.mjs
@@ -151,7 +151,13 @@ export let COMMAND_FIREFOX_VIEW = "fxaccounts:firefox_view";
// be stored. This branch will be reset on account signout and signin.
export let PREF_ACCOUNT_ROOT = "identity.fxaccounts.account.";
-export let PREF_LAST_FXA_USER = "identity.fxaccounts.lastSignedInUserHash";
+// Where we store the hashed uid of the previous user.
+export let PREF_LAST_FXA_USER_UID =
+ "identity.fxaccounts.lastSignedInUserIdHash";
+// Where we used to store the hashed email of the previous user. We now store the
+// uid, but need to migrate from this.
+export let PREF_LAST_FXA_USER_EMAIL =
+ "identity.fxaccounts.lastSignedInUserHash";
export let PREF_REMOTE_PAIRING_URI = "identity.fxaccounts.remote.pairing.uri";
// Server errno.
diff --git a/services/fxaccounts/FxAccountsWebChannel.sys.mjs b/services/fxaccounts/FxAccountsWebChannel.sys.mjs
@@ -30,7 +30,8 @@ import {
COMMAND_FIREFOX_VIEW,
OAUTH_CLIENT_ID,
ON_PROFILE_CHANGE_NOTIFICATION,
- PREF_LAST_FXA_USER,
+ PREF_LAST_FXA_USER_UID,
+ PREF_LAST_FXA_USER_EMAIL,
WEBCHANNEL_ID,
log,
logPII,
@@ -300,16 +301,15 @@ FxAccountsWebChannel.prototype = {
{
let response = { command, messageId: message.messageId };
// If browser profiles are not enabled, then we use the old merge sync dialog
- if (!lazy.SelectableProfileService?.isEnabled) {
- response.data = { ok: this._helpers.shouldAllowRelink(data.email) };
+ if (!this._helpers._selectableProfilesEnabled()) {
+ response.data = { ok: this._helpers.shouldAllowRelink(data) };
this._channel.send(response, sendingContext);
break;
}
// In the new sync warning, we give users a few more options to
// control what they want to do with their sync data
- let result = await this._helpers.promptProfileSyncWarningIfNeeded(
- data.email
- );
+ let result =
+ await this._helpers.promptProfileSyncWarningIfNeeded(data);
switch (result.action) {
case "create-profile":
lazy.SelectableProfileService.createNewProfile();
@@ -517,9 +517,10 @@ FxAccountsWebChannelHelpers.prototype = {
// (This is sync-specific, so ideally would be in sync's identity module,
// but it's a little more seamless to do here, and sync is currently the
// only fxa consumer, so...
- shouldAllowRelink(acctName) {
+ shouldAllowRelink(acctData) {
return (
- !this._needRelinkWarning(acctName) || this._promptForRelink(acctName)
+ !this._needRelinkWarning(acctData) ||
+ this._promptForRelink(acctData.email)
);
},
@@ -530,14 +531,15 @@ FxAccountsWebChannelHelpers.prototype = {
* @returns {string} - The corresponding option the user pressed. Can be either:
* cancel, continue, switch-profile, or create-profile
*/
- async promptProfileSyncWarningIfNeeded(acctEmail) {
+ async promptProfileSyncWarningIfNeeded(acctData) {
// Was a previous account signed into this profile or is there another profile currently signed in
// to the account we're signing into
- let profileLinkedWithAcct =
- await this._getProfileAssociatedWithAcct(acctEmail);
- if (this._needRelinkWarning(acctEmail) || profileLinkedWithAcct) {
+ let profileLinkedWithAcct = acctData.uid
+ ? await this._getProfileAssociatedWithAcct(acctData.uid)
+ : null;
+ if (this._needRelinkWarning(acctData) || profileLinkedWithAcct) {
return this._promptForProfileSyncWarning(
- acctEmail,
+ acctData.email,
profileLinkedWithAcct
);
}
@@ -664,23 +666,22 @@ FxAccountsWebChannelHelpers.prototype = {
log.debug(`storing info for services ${Object.keys(requestedServices)}`);
accountData.requestedServices = JSON.stringify(requestedServices);
- this.setPreviousAccountNameHashPref(accountData.email);
+ this.setPreviousAccountHashPref(accountData.uid);
await this._fxAccounts._internal.setSignedInUser(accountData);
log.debug("Webchannel finished logging a user in.");
},
/**
- * Logins in to sync by completing an OAuth flow
+ * Logs in to sync by completing an OAuth flow
*
* @param {object} oauthData: The oauth code and state as returned by the server
*/
async oauthLogin(oauthData) {
log.debug("Webchannel is completing the oauth flow");
- const { uid, sessionToken, email, requestedServices } =
+ const { uid, sessionToken, requestedServices } =
await this._fxAccounts._internal.getUserAccountData([
"uid",
"sessionToken",
- "email",
"requestedServices",
]);
// First we finish the ongoing oauth flow
@@ -695,7 +696,7 @@ FxAccountsWebChannelHelpers.prototype = {
await this._fxAccounts._internal.destroyOAuthToken({ token: refreshToken });
// Remember the account for future merge warnings etc.
- this.setPreviousAccountNameHashPref(email);
+ this.setPreviousAccountHashPref(uid);
if (!scopedKeys) {
log.info(
@@ -855,6 +856,7 @@ FxAccountsWebChannelHelpers.prototype = {
// This capability is for telling FxA that the current build can accept
// accounts without passwords/sync keys (third-party auth)
keys_optional: true,
+ can_link_account_uid: true,
engines,
};
},
@@ -890,26 +892,22 @@ FxAccountsWebChannelHelpers.prototype = {
},
/**
- * Get the hash of account name of the previously signed in account
- */
- getPreviousAccountNameHashPref() {
- try {
- return Services.prefs.getStringPref(PREF_LAST_FXA_USER);
- } catch (_) {
- return "";
- }
- },
-
- /**
- * Given an account name, set the hash of the previously signed in account
+ * Remember that a particular account id was previously signed in to this device.
*
- * @param acctName the account name of the user's account.
+ * @param uid the account uid
*/
- setPreviousAccountNameHashPref(acctName) {
+ setPreviousAccountHashPref(uid) {
+ if (!uid) {
+ throw new Error("No uid specified");
+ }
Services.prefs.setStringPref(
- PREF_LAST_FXA_USER,
- lazy.CryptoUtils.sha256Base64(acctName)
+ PREF_LAST_FXA_USER_UID,
+ lazy.CryptoUtils.sha256Base64(uid)
);
+ // This should not be necessary but exists just to be safe, to avoid
+ // any possibility we somehow end up with *both* prefs set and each indicating
+ // a different account.
+ Services.prefs.clearUserPref(PREF_LAST_FXA_USER_EMAIL);
},
/**
@@ -946,10 +944,46 @@ FxAccountsWebChannelHelpers.prototype = {
*
* @private
*/
- _needRelinkWarning(acctName) {
- let prevAcctHash = this.getPreviousAccountNameHashPref();
+ _needRelinkWarning(acctData) {
+ // This code *never* expects both PREF_LAST_FXA_USER_EMAIL and PREF_LAST_FXA_USER_UID.
+ // * If we have PREF_LAST_FXA_USER_EMAIL it means we were signed out before we migrated
+ // to UID, and can't learn that UID, so have no UID pref set.
+ // * If the UID pref exists, our code since that landed will never write to the
+ // PREF_LAST_FXA_USER_EMAIL pref.
+ // The only way both could be true would be something catastrophic, such as our
+ // "migrate to uid at sign-out" code somehow died between writing the UID and
+ // clearing the email.
+ //
+ // Therefore, we don't even try to handle both being set, but do prefer the UID
+ // because that must have been written by the new code paths introduced for that pref.
+ const lastUid = Services.prefs.getStringPref(PREF_LAST_FXA_USER_UID, "");
+ if (lastUid) {
+ // A special case here is for when no uid is specified by the server - that means the
+ // server is about to create a new account. Therefore, the new account can't possibly
+ // match.
+ return (
+ !acctData.uid || lastUid != lazy.CryptoUtils.sha256Base64(acctData.uid)
+ );
+ }
+
+ // no uid pref, check if there's an EMAIL pref (which means a user previously signed out
+ // before we landed this uid-aware code, so only know their email.)
+ const lastEmail = Services.prefs.getStringPref(
+ PREF_LAST_FXA_USER_EMAIL,
+ ""
+ );
+ return (
+ lastEmail && lastEmail != lazy.CryptoUtils.sha256Base64(acctData.email)
+ );
+ },
+
+ // Does this install have multiple profiles available? The SelectableProfileService
+ // being enabled isn't enough, because this doesn't tell us whether a new profile
+ // as actually created!
+ _selectableProfilesEnabled() {
return (
- prevAcctHash && prevAcctHash != lazy.CryptoUtils.sha256Base64(acctName)
+ lazy.SelectableProfileService?.isEnabled &&
+ lazy.SelectableProfileService?.hasCreatedSelectableProfiles()
);
},
@@ -965,10 +999,10 @@ FxAccountsWebChannelHelpers.prototype = {
/**
* Checks if a profile is associated with the given account email.
*
- * @param {string} acctEmail - The email of the account to check.
+ * @param {string} acctUid - The uid of the account to check.
* @returns {Promise<SelectableProfile|null>} - The profile associated with the account, or null if none.
*/
- async _getProfileAssociatedWithAcct(acctEmail) {
+ async _getProfileAssociatedWithAcct(acctUid) {
let profiles = await this._getAllProfiles();
let currentProfileName = await this._getCurrentProfileName();
for (let profile of profiles) {
@@ -981,7 +1015,7 @@ FxAccountsWebChannelHelpers.prototype = {
let signedInUser = await this._readJSONFileAsync(signedInUserPath);
if (
signedInUser?.accountData &&
- signedInUser.accountData.email === acctEmail
+ signedInUser.accountData.uid === acctUid
) {
// The account is signed into another profile
return profile;
@@ -1012,7 +1046,7 @@ FxAccountsWebChannelHelpers.prototype = {
*
* @private
*/
- _promptForRelink(acctName) {
+ _promptForRelink(acctEmail) {
let [continueLabel, title, heading, description] =
lazy.l10n.formatValuesSync([
{ id: "sync-setup-verify-continue" },
@@ -1021,7 +1055,7 @@ FxAccountsWebChannelHelpers.prototype = {
{
id: "sync-setup-verify-description",
args: {
- email: acctName,
+ email: acctEmail,
},
},
]);
@@ -1242,7 +1276,7 @@ FxAccountsWebChannelHelpers.prototype = {
) {
let variant;
- if (!lazy.SelectableProfileService?.isEnabled) {
+ if (!this._selectableProfilesEnabled()) {
// Old merge dialog
variant = "old-merge";
} else if (isAccountLoggedIntoAnotherProfile) {
diff --git a/services/fxaccounts/tests/xpcshell/test_sync_warning_dialogs.js b/services/fxaccounts/tests/xpcshell/test_sync_warning_dialogs.js
@@ -8,6 +8,7 @@ ChromeUtils.defineESModuleGetters(this, {
"resource://gre/modules/FxAccountsWebChannel.sys.mjs",
SelectableProfileService:
"resource:///modules/profiles/SelectableProfileService.sys.mjs",
+ PREF_LAST_FXA_USER_UID: "resource://gre/modules/FxAccountsCommon.sys.mjs",
});
// Set up mocked profiles
@@ -124,9 +125,11 @@ add_task(
let helpers = new FxAccountsWebChannelHelpers();
// We "pretend" there was another account previously logged in
- helpers.setPreviousAccountNameHashPref("testuser@testuser.com");
+ helpers.setPreviousAccountHashPref("test_uid");
// Mock methods
+ helpers._selectableProfilesEnabled = () =>
+ Services.prefs.getBoolPref("browser.profiles.enabled");
helpers._getAllProfiles = async () => mockedProfiles;
helpers._getCurrentProfileName = () => mockedProfiles[0].name;
helpers._readJSONFileAsync = async function (_filePath) {
@@ -145,9 +148,11 @@ add_task(
variant.expectedResponses[i];
gResponse = responseVal;
- let result =
- await helpers.promptProfileSyncWarningIfNeeded("testuser2@test.com");
- //Verify we returned the expected result
+ let result = await helpers.promptProfileSyncWarningIfNeeded({
+ email: "testuser2@test.com",
+ uid: "test2",
+ });
+ // Verify we returned the expected result
Assert.deepEqual(result, expectedResult);
let gleanValue = Glean.syncMergeDialog.clicked.testGetValue();
@@ -262,6 +267,8 @@ add_task(
let helpers = new FxAccountsWebChannelHelpers();
// Mock methods
+ helpers._selectableProfilesEnabled = () =>
+ Services.prefs.getBoolPref("browser.profiles.enabled");
helpers._getAllProfiles = async () => mockedProfiles;
helpers._getCurrentProfileName = () => mockedProfiles[0].name;
// Mock the file reading to simulate the account being signed into the other profile
@@ -273,7 +280,7 @@ add_task(
// The account is signed into the other profile
return {
version: 1,
- accountData: { email: "testuser2@test.com" },
+ accountData: { email: "testuser2@test.com", uid: "uid" },
};
}
return null;
@@ -291,9 +298,11 @@ add_task(
variant.expectedResponses[i];
gResponse = responseVal;
- let result =
- await helpers.promptProfileSyncWarningIfNeeded("testuser2@test.com");
- //Verify we returned the expected result
+ let result = await helpers.promptProfileSyncWarningIfNeeded({
+ email: "testuser2@test.com",
+ uid: "uid",
+ });
+ // Verify we returned the expected result
Assert.deepEqual(result, expectedResult);
let gleanValue = Glean.syncMergeDialog.clicked.testGetValue();
@@ -331,11 +340,11 @@ add_task(async function test_current_profile_is_correctly_skipped() {
// Profile2 is signed in with other@example.com.
const fakeSignedInUsers = {
[PathUtils.join(PathUtils.tempDir, "profile1", "signedInUser.json")]: {
- accountData: { email: "user@example.com" },
+ accountData: { email: "user@example.com", uid: "user" },
version: 1,
},
[PathUtils.join(PathUtils.tempDir, "profile2", "signedInUser.json")]: {
- accountData: { email: "other@example.com" },
+ accountData: { email: "other@example.com", uid: "other" },
version: 1,
},
};
@@ -350,8 +359,7 @@ add_task(async function test_current_profile_is_correctly_skipped() {
fakeSignedInUsers[filePath] || null;
// Case 1: The account email is in the current profile.
- let associatedProfile =
- await channel._getProfileAssociatedWithAcct("user@example.com");
+ let associatedProfile = await channel._getProfileAssociatedWithAcct("user");
Assert.equal(
associatedProfile,
null,
@@ -359,8 +367,7 @@ add_task(async function test_current_profile_is_correctly_skipped() {
);
// Case 2: The account email is in a different profile.
- associatedProfile =
- await channel._getProfileAssociatedWithAcct("other@example.com");
+ associatedProfile = await channel._getProfileAssociatedWithAcct("other");
Assert.ok(
associatedProfile,
"Should return a profile when account email is in another profile."
@@ -371,3 +378,30 @@ add_task(async function test_current_profile_is_correctly_skipped() {
"Returned profile should be 'Profile2'."
);
});
+
+// Test need-relink-warning.
+add_task(
+ async function test_previously_signed_in_dialog_variants_result_and_telemetry() {
+ let helpers = new FxAccountsWebChannelHelpers();
+
+ // We "pretend" there was another account previously logged in
+ helpers.setPreviousAccountHashPref("test_uid");
+
+ Assert.ok(
+ !helpers._needRelinkWarning({
+ email: "testuser2@test.com",
+ uid: "test_uid",
+ })
+ );
+ Assert.ok(
+ helpers._needRelinkWarning({
+ email: "testuser2@test.com",
+ uid: "different_uid",
+ })
+ );
+ // missing uid == "new account" == "always need the warning if anyone was previously logged in"
+ Assert.ok(helpers._needRelinkWarning({ email: "testuser2@test.com" }));
+ Services.prefs.clearUserPref(PREF_LAST_FXA_USER_UID);
+ Assert.ok(!helpers._needRelinkWarning({ email: "testuser2@test.com" }));
+ }
+);
diff --git a/services/fxaccounts/tests/xpcshell/test_web_channel.js b/services/fxaccounts/tests/xpcshell/test_web_channel.js
@@ -13,6 +13,9 @@ const { FxAccountsWebChannel, FxAccountsWebChannelHelpers } =
"resource://gre/modules/FxAccountsWebChannel.sys.mjs"
);
+const { PREF_LAST_FXA_USER_EMAIL, PREF_LAST_FXA_USER_UID } =
+ ChromeUtils.importESModule("resource://gre/modules/FxAccountsCommon.sys.mjs");
+
const URL_STRING = "https://example.com";
const mockSendingContext = {
@@ -63,7 +66,7 @@ add_task(async function test_rejection_reporting() {
let mockMessage = {
command: "fxaccounts:login",
messageId: "1234",
- data: { email: "testuser@testuser.com" },
+ data: { email: "testuser@testuser.com", uid: "testuser" },
};
let channel = new FxAccountsWebChannel({
@@ -332,19 +335,18 @@ add_test(function test_delete_message() {
add_test(function test_can_link_account_message() {
let mockMessage = {
command: "fxaccounts:can_link_account",
- data: { email: "testuser@testuser.com" },
+ data: { email: "testuser@testuser.com", uid: "testuser" },
};
let channel = new FxAccountsWebChannel({
channel_id: WEBCHANNEL_ID,
content_uri: URL_STRING,
helpers: {
- shouldAllowRelink(email) {
- Assert.equal(email, "testuser@testuser.com");
- run_next_test();
+ _selectableProfilesEnabled() {
+ return false;
},
- promptProfileSyncWarningIfNeeded(acctName) {
- Assert.equal(acctName, "testuser@testuser.com");
+ shouldAllowRelink(acctData) {
+ Assert.deepEqual(acctData, mockMessage.data);
run_next_test();
},
},
@@ -435,45 +437,7 @@ add_test(function test_fxa_status_message() {
channel._channelCallback(WEBCHANNEL_ID, mockMessage, mockSendingContext);
});
-add_test(function test_respond_to_device_commands() {
- let mockMessageLoggedOut = {
- command: "fxaccounts:logout",
- messageId: 123,
- data: {},
- };
- let mockMessageLoggedIn = {
- command: "fxaccounts:login",
- messageId: 123,
- data: {},
- };
-
- let channel = new FxAccountsWebChannel({
- channel_id: WEBCHANNEL_ID,
- content_uri: URL_STRING,
- });
- channel._channel = {
- send(response) {
- Assert.ok(!!response.data);
- Assert.equal(response.data.ok, true);
-
- run_next_test();
- },
- };
-
- channel._channelCallback(
- WEBCHANNEL_ID,
- mockMessageLoggedOut,
- mockSendingContext
- );
-
- channel._channelCallback(
- WEBCHANNEL_ID,
- mockMessageLoggedIn,
- mockSendingContext
- );
-});
-
-add_test(function test_respond_to_incorrect_device_commands() {
+add_test(function test_respond_to_invalid_commands() {
let mockMessageLogout = {
command: "fxaccounts:lagaut", // intentional typo.
messageId: 123,
@@ -517,11 +481,16 @@ add_test(function test_unrecognized_message() {
run_next_test();
});
-add_test(function test_helpers_should_allow_relink_same_email() {
+add_test(function test_helpers_should_allow_relink_same_account() {
let helpers = new FxAccountsWebChannelHelpers();
- helpers.setPreviousAccountNameHashPref("testuser@testuser.com");
- Assert.ok(helpers.shouldAllowRelink("testuser@testuser.com"));
+ helpers.setPreviousAccountHashPref("testuser");
+ Assert.ok(
+ helpers.shouldAllowRelink({
+ email: "testuser@testuser.com",
+ uid: "testuser",
+ })
+ );
run_next_test();
});
@@ -529,14 +498,24 @@ add_test(function test_helpers_should_allow_relink_same_email() {
add_test(function test_helpers_should_allow_relink_different_email() {
let helpers = new FxAccountsWebChannelHelpers();
- helpers.setPreviousAccountNameHashPref("testuser@testuser.com");
+ helpers.setPreviousAccountHashPref("testuser");
helpers._promptForRelink = acctName => {
return acctName === "allowed_to_relink@testuser.com";
};
- Assert.ok(helpers.shouldAllowRelink("allowed_to_relink@testuser.com"));
- Assert.ok(!helpers.shouldAllowRelink("not_allowed_to_relink@testuser.com"));
+ Assert.ok(
+ helpers.shouldAllowRelink({
+ uid: "uid",
+ email: "allowed_to_relink@testuser.com",
+ })
+ );
+ Assert.ok(
+ !helpers.shouldAllowRelink({
+ uid: "uid",
+ email: "not_allowed_to_relink@testuser.com",
+ })
+ );
run_next_test();
});
@@ -575,9 +554,10 @@ add_task(async function test_helpers_login_without_customize_sync() {
});
// ensure the previous account pref is overwritten.
- helpers.setPreviousAccountNameHashPref("lastuser@testuser.com");
+ helpers.setPreviousAccountHashPref("lastuser");
await helpers.login({
+ uid: "testuser",
email: "testuser@testuser.com",
verifiedCanLinkAccount: true,
customizeSync: false,
@@ -587,7 +567,7 @@ add_task(async function test_helpers_login_without_customize_sync() {
);
});
-add_task(async function test_helpers_login_set_previous_account_name_hash() {
+add_task(async function test_helpers_login_set_previous_account_hash() {
let helpers = new FxAccountsWebChannelHelpers({
fxAccounts: {
getSignedInUser() {
@@ -598,8 +578,12 @@ add_task(async function test_helpers_login_set_previous_account_name_hash() {
return new Promise(resolve => {
// previously signed in user preference is updated.
Assert.equal(
- helpers.getPreviousAccountNameHashPref(),
- CryptoUtils.sha256Base64("newuser@testuser.com")
+ Services.prefs.getStringPref(PREF_LAST_FXA_USER_UID),
+ CryptoUtils.sha256Base64("new_uid")
+ );
+ Assert.equal(
+ Services.prefs.getStringPref(PREF_LAST_FXA_USER_EMAIL, ""),
+ ""
);
resolve();
});
@@ -620,9 +604,10 @@ add_task(async function test_helpers_login_set_previous_account_name_hash() {
});
// ensure the previous account pref is overwritten.
- helpers.setPreviousAccountNameHashPref("lastuser@testuser.com");
+ helpers.setPreviousAccountHashPref("last_uid");
await helpers.login({
+ uid: "new_uid",
email: "newuser@testuser.com",
verifiedCanLinkAccount: true,
customizeSync: false,
@@ -661,6 +646,7 @@ add_task(async function test_helpers_login_another_user_signed_in() {
helpers._disconnect = sinon.spy();
await helpers.login({
+ uid: "testuser",
email: "testuser@testuser.com",
verifiedCanLinkAccount: true,
customizeSync: false,
@@ -705,6 +691,7 @@ add_task(async function test_helpers_login_with_customize_sync() {
});
await helpers.login({
+ uid: "testuser",
email: "testuser@testuser.com",
verifiedCanLinkAccount: true,
customizeSync: true,
diff --git a/services/sync/modules/SyncDisconnect.sys.mjs b/services/sync/modules/SyncDisconnect.sys.mjs
@@ -10,7 +10,8 @@ const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
AsyncShutdown: "resource://gre/modules/AsyncShutdown.sys.mjs",
Log: "resource://gre/modules/Log.sys.mjs",
- PREF_LAST_FXA_USER: "resource://gre/modules/FxAccountsCommon.sys.mjs",
+ PREF_LAST_FXA_USER_EMAIL: "resource://gre/modules/FxAccountsCommon.sys.mjs",
+ PREF_LAST_FXA_USER_UID: "resource://gre/modules/FxAccountsCommon.sys.mjs",
Sanitizer: "resource:///modules/Sanitizer.sys.mjs",
Utils: "resource://services-sync/util.sys.mjs",
setTimeout: "resource://gre/modules/Timer.sys.mjs",
@@ -100,7 +101,8 @@ export const SyncDisconnectInternal = {
// Reset the pref which is used to show a warning when a different user
// signs in - this is no longer a concern now that we've removed the
// data from the profile.
- Services.prefs.clearUserPref(lazy.PREF_LAST_FXA_USER);
+ Services.prefs.clearUserPref(lazy.PREF_LAST_FXA_USER_EMAIL);
+ Services.prefs.clearUserPref(lazy.PREF_LAST_FXA_USER_UID);
log.info("Finished wiping sync data");
} catch (ex) {
diff --git a/services/sync/tests/unit/test_disconnect_shutdown.js b/services/sync/tests/unit/test_disconnect_shutdown.js
@@ -9,7 +9,7 @@ const { SyncDisconnect, SyncDisconnectInternal } = ChromeUtils.importESModule(
const { AsyncShutdown } = ChromeUtils.importESModule(
"resource://gre/modules/AsyncShutdown.sys.mjs"
);
-const { PREF_LAST_FXA_USER } = ChromeUtils.importESModule(
+const { PREF_LAST_FXA_USER_UID } = ChromeUtils.importESModule(
"resource://gre/modules/FxAccountsCommon.sys.mjs"
);
@@ -58,7 +58,10 @@ add_task(async function test_shutdown_blocker() {
let weaveStub = sinon.stub(SyncDisconnectInternal, "getWeave");
weaveStub.returns(Weave);
- Services.prefs.setStringPref(PREF_LAST_FXA_USER, "dGVzdEBleGFtcGxlLmNvbQ==");
+ Services.prefs.setStringPref(
+ PREF_LAST_FXA_USER_UID,
+ "dGVzdEBleGFtcGxlLmNvbQ=="
+ );
let promiseDisconnected = SyncDisconnect.disconnect(true);
@@ -72,7 +75,7 @@ add_task(async function test_shutdown_blocker() {
await promiseDisconnected;
Assert.ok(
- !Services.prefs.prefHasUserValue(PREF_LAST_FXA_USER),
+ !Services.prefs.prefHasUserValue(PREF_LAST_FXA_USER_UID),
"Should have reset different user warning pref"
);
Assert.equal(