Enable DeviceOAuth2TokenService on desktop platforms

This change adds a desktop (win/linux/mac) implementation of
DeviceOAuth2TokenStore, and enables compilation of
DeviceOAuth2TokenService (and related classes) on desktop platforms.

This should allow getting and storing a refresh token for the service
account passed to the browser through the Chrome Policy data. See
go/cbcm-machine-policy-invalidations for context about this change.

Bug: 1026261

Change-Id: I44a1f7fe30640dec0886cbe881a4f5a335c23fa2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2067624
Commit-Queue: anthonyvd <[email protected]>
Reviewed-by: Mihai Sardarescu <[email protected]>
Reviewed-by: Gabriel Charette <[email protected]>
Reviewed-by: Rebekah Potter <[email protected]>
Reviewed-by: Marc Treib <[email protected]>
Reviewed-by: Pavol Marko <[email protected]>
Reviewed-by: Julian Pastarmov <[email protected]>
Cr-Commit-Position: refs/heads/master@{#752973}
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index c4117c7..f97a214 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -3144,6 +3144,13 @@
       "component_updater/intervention_policy_database_component_installer.h",
       "custom_handlers/register_protocol_handler_permission_request.cc",
       "custom_handlers/register_protocol_handler_permission_request.h",
+      "device_identity/device_identity_provider.cc",
+      "device_identity/device_identity_provider.h",
+      "device_identity/device_oauth2_token_service.cc",
+      "device_identity/device_oauth2_token_service.h",
+      "device_identity/device_oauth2_token_service_factory.cc",
+      "device_identity/device_oauth2_token_service_factory.h",
+      "device_identity/device_oauth2_token_store.h",
       "diagnostics/diagnostics_controller.cc",
       "diagnostics/diagnostics_controller.h",
       "diagnostics/diagnostics_metrics.cc",
@@ -3829,13 +3836,6 @@
       "component_updater/smart_dim_component_installer.h",
       "device_identity/chromeos/device_oauth2_token_store_chromeos.cc",
       "device_identity/chromeos/device_oauth2_token_store_chromeos.h",
-      "device_identity/device_identity_provider.cc",
-      "device_identity/device_identity_provider.h",
-      "device_identity/device_oauth2_token_service.cc",
-      "device_identity/device_oauth2_token_service.h",
-      "device_identity/device_oauth2_token_service_factory.cc",
-      "device_identity/device_oauth2_token_service_factory.h",
-      "device_identity/device_oauth2_token_store.h",
       "download/notification/download_item_notification.cc",
       "download/notification/download_item_notification.h",
       "download/notification/download_notification_manager.cc",
@@ -4319,6 +4319,8 @@
 
   if (!is_android && !is_chromeos) {
     sources += [
+      "device_identity/device_oauth2_token_store_desktop.cc",
+      "device_identity/device_oauth2_token_store_desktop.h",
       "first_run/upgrade_util.cc",
       "first_run/upgrade_util.h",
       "first_run/upgrade_util_mac.cc",
diff --git a/chrome/browser/chromeos/policy/affiliated_invalidation_service_provider_impl.cc b/chrome/browser/chromeos/policy/affiliated_invalidation_service_provider_impl.cc
index aec7111..c31f521 100644
--- a/chrome/browser/chromeos/policy/affiliated_invalidation_service_provider_impl.cc
+++ b/chrome/browser/chromeos/policy/affiliated_invalidation_service_provider_impl.cc
@@ -428,9 +428,8 @@
     DCHECK(url_loader_factory);
   }
 
-  device_identity_provider_ =
-      std::make_unique<chromeos::DeviceIdentityProvider>(
-          chromeos::DeviceOAuth2TokenServiceFactory::Get());
+  device_identity_provider_ = std::make_unique<DeviceIdentityProvider>(
+      DeviceOAuth2TokenServiceFactory::Get());
 
   device_instance_id_driver_ = std::make_unique<instance_id::InstanceIDDriver>(
       g_browser_process->gcm_driver());
diff --git a/chrome/browser/chromeos/policy/affiliated_invalidation_service_provider_impl_unittest.cc b/chrome/browser/chromeos/policy/affiliated_invalidation_service_provider_impl_unittest.cc
index 2820239..2fb1a80 100644
--- a/chrome/browser/chromeos/policy/affiliated_invalidation_service_provider_impl_unittest.cc
+++ b/chrome/browser/chromeos/policy/affiliated_invalidation_service_provider_impl_unittest.cc
@@ -221,7 +221,7 @@
   chromeos::CryptohomeClient::InitializeFake();
   ASSERT_TRUE(profile_manager_.SetUp());
 
-  chromeos::DeviceOAuth2TokenServiceFactory::Initialize(
+  DeviceOAuth2TokenServiceFactory::Initialize(
       test_url_loader_factory_.GetSafeWeakWrapper(),
       TestingBrowserProcess::GetGlobal()->local_state());
 
@@ -240,7 +240,7 @@
   invalidation::ProfileInvalidationProviderFactory::GetInstance()
       ->RegisterTestingFactory(
           BrowserContextKeyedServiceFactory::TestingFactory());
-  chromeos::DeviceOAuth2TokenServiceFactory::Shutdown();
+  DeviceOAuth2TokenServiceFactory::Shutdown();
   chromeos::CryptohomeClient::Shutdown();
   chromeos::SystemSaltGetter::Shutdown();
 }
diff --git a/chrome/browser/chromeos/policy/device_account_initializer.cc b/chrome/browser/chromeos/policy/device_account_initializer.cc
index 97a11dfb..b395d56 100644
--- a/chrome/browser/chromeos/policy/device_account_initializer.cc
+++ b/chrome/browser/chromeos/policy/device_account_initializer.cc
@@ -138,7 +138,7 @@
 
 void DeviceAccountInitializer::StoreToken() {
   handling_request_ = true;
-  chromeos::DeviceOAuth2TokenServiceFactory::Get()->SetAndSaveRefreshToken(
+  DeviceOAuth2TokenServiceFactory::Get()->SetAndSaveRefreshToken(
       robot_refresh_token_,
       base::AdaptCallbackForRepeating(base::BindOnce(
           &DeviceAccountInitializer::HandleStoreRobotAuthTokenResult,
diff --git a/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos_unittest.cc b/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos_unittest.cc
index f3989be1..ca5c339 100644
--- a/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos_unittest.cc
+++ b/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos_unittest.cc
@@ -184,7 +184,7 @@
 
     // SystemSaltGetter is used in DeviceOAuth2TokenService.
     chromeos::SystemSaltGetter::Initialize();
-    chromeos::DeviceOAuth2TokenServiceFactory::Initialize(
+    DeviceOAuth2TokenServiceFactory::Initialize(
         test_url_loader_factory_.GetSafeWeakWrapper(), &local_state_);
 
     url_fetcher_response_code_ = net::HTTP_OK;
@@ -205,7 +205,7 @@
     manager_.reset();
     install_attributes_.reset();
 
-    chromeos::DeviceOAuth2TokenServiceFactory::Shutdown();
+    DeviceOAuth2TokenServiceFactory::Shutdown();
     chromeos::SystemSaltGetter::Shutdown();
     TestingBrowserProcess::GetGlobal()->SetLocalState(nullptr);
 
@@ -685,8 +685,8 @@
       // Verify the state only if the task is not yet failed.
       // Note that, if the flow is not yet |done_| here, assume that it is
       // in the "succeeding" flow, so verify here, too.
-      chromeos::DeviceOAuth2TokenService* token_service =
-          chromeos::DeviceOAuth2TokenServiceFactory::Get();
+      DeviceOAuth2TokenService* token_service =
+          DeviceOAuth2TokenServiceFactory::Get();
 
       // For the refresh token for the robot account to be visible, the robot
       // account ID must not be empty.
diff --git a/chrome/browser/chromeos/policy/remote_commands/crd_host_delegate.cc b/chrome/browser/chromeos/policy/remote_commands/crd_host_delegate.cc
index 9d6a9c0..2f6efcf 100644
--- a/chrome/browser/chromeos/policy/remote_commands/crd_host_delegate.cc
+++ b/chrome/browser/chromeos/policy/remote_commands/crd_host_delegate.cc
@@ -92,8 +92,7 @@
 CRDHostDelegate::CRDHostDelegate()
     : OAuth2AccessTokenManager::Consumer("crd_host_delegate") {}
 
-CRDHostDelegate::~CRDHostDelegate() {
-}
+CRDHostDelegate::~CRDHostDelegate() {}
 
 bool CRDHostDelegate::HasActiveSession() const {
   return host_ != nullptr;
@@ -108,7 +107,7 @@
   return user_manager::UserManager::IsInitialized() &&
          ui::UserActivityDetector::Get() != nullptr &&
          chromeos::ProfileHelper::Get() != nullptr &&
-         chromeos::DeviceOAuth2TokenServiceFactory::Get() != nullptr;
+         DeviceOAuth2TokenServiceFactory::Get() != nullptr;
 }
 
 bool CRDHostDelegate::IsRunningKiosk() const {
@@ -147,8 +146,8 @@
     DeviceCommandStartCRDSessionJob::ErrorCallback error_callback) {
   DCHECK(!oauth_success_callback_);
   DCHECK(!error_callback_);
-  chromeos::DeviceOAuth2TokenService* oauth_service =
-      chromeos::DeviceOAuth2TokenServiceFactory::Get();
+  DeviceOAuth2TokenService* oauth_service =
+      DeviceOAuth2TokenServiceFactory::Get();
 
   OAuth2AccessTokenManager::ScopeSet scopes;
   scopes.insert(GaiaConstants::kGoogleUserInfoEmail);
@@ -196,7 +195,7 @@
   // Store all parameters for future connect call.
   base::Value connect_params(base::Value::Type::DICTIONARY);
   CoreAccountId account_id =
-      chromeos::DeviceOAuth2TokenServiceFactory::Get()->GetRobotAccountId();
+      DeviceOAuth2TokenServiceFactory::Get()->GetRobotAccountId();
 
   // TODO(msarda): This conversion will not be correct once account id is
   // migrated to be the Gaia ID on ChromeOS. Fix it.
diff --git a/chrome/browser/chromeos/policy/remote_commands/screenshot_delegate.cc b/chrome/browser/chromeos/policy/remote_commands/screenshot_delegate.cc
index b66d3f0..ac23eea 100644
--- a/chrome/browser/chromeos/policy/remote_commands/screenshot_delegate.cc
+++ b/chrome/browser/chromeos/policy/remote_commands/screenshot_delegate.cc
@@ -24,8 +24,7 @@
 
 ScreenshotDelegate::ScreenshotDelegate() {}
 
-ScreenshotDelegate::~ScreenshotDelegate() {
-}
+ScreenshotDelegate::~ScreenshotDelegate() {}
 
 bool ScreenshotDelegate::IsScreenshotAllowed() {
   BrowserPolicyConnectorChromeOS* connector =
@@ -52,8 +51,8 @@
 std::unique_ptr<UploadJob> ScreenshotDelegate::CreateUploadJob(
     const GURL& upload_url,
     UploadJob::Delegate* delegate) {
-  chromeos::DeviceOAuth2TokenService* device_oauth2_token_service =
-      chromeos::DeviceOAuth2TokenServiceFactory::Get();
+  DeviceOAuth2TokenService* device_oauth2_token_service =
+      DeviceOAuth2TokenServiceFactory::Get();
 
   CoreAccountId robot_account_id =
       device_oauth2_token_service->GetRobotAccountId();
diff --git a/chrome/browser/chromeos/policy/system_log_uploader.cc b/chrome/browser/chromeos/policy/system_log_uploader.cc
index 21f0f3f4..5f856ef5 100644
--- a/chrome/browser/chromeos/policy/system_log_uploader.cc
+++ b/chrome/browser/chromeos/policy/system_log_uploader.cc
@@ -205,8 +205,8 @@
 std::unique_ptr<UploadJob> SystemLogDelegate::CreateUploadJob(
     const GURL& upload_url,
     UploadJob::Delegate* delegate) {
-  chromeos::DeviceOAuth2TokenService* device_oauth2_token_service =
-      chromeos::DeviceOAuth2TokenServiceFactory::Get();
+  DeviceOAuth2TokenService* device_oauth2_token_service =
+      DeviceOAuth2TokenServiceFactory::Get();
 
   CoreAccountId robot_account_id =
       device_oauth2_token_service->GetRobotAccountId();
diff --git a/chrome/browser/device_identity/chromeos/device_oauth2_token_store_chromeos.cc b/chrome/browser/device_identity/chromeos/device_oauth2_token_store_chromeos.cc
index ed8fb33..385af0e 100644
--- a/chrome/browser/device_identity/chromeos/device_oauth2_token_store_chromeos.cc
+++ b/chrome/browser/device_identity/chromeos/device_oauth2_token_store_chromeos.cc
@@ -9,6 +9,7 @@
 #include "chrome/common/pref_names.h"
 #include "chromeos/cryptohome/system_salt_getter.h"
 #include "chromeos/settings/cros_settings_names.h"
+#include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 
 namespace chromeos {
@@ -26,6 +27,13 @@
   FlushTokenSaveCallbacks(false);
 }
 
+// static
+void DeviceOAuth2TokenStoreChromeOS::RegisterPrefs(
+    PrefRegistrySimple* registry) {
+  registry->RegisterStringPref(prefs::kDeviceRobotAnyApiRefreshToken,
+                               std::string());
+}
+
 void DeviceOAuth2TokenStoreChromeOS::Init(InitCallback callback) {
   state_ = State::INITIALIZING;
   // Pull in the system salt.
@@ -51,7 +59,7 @@
   // If the robot account ID is not available yet, do not announce the token. It
   // will be done from OnServiceAccountIdentityChanged() once the robot account
   // ID becomes available as well.
-  if (!GetAccountId().empty())
+  if (observer() && !GetAccountId().empty())
     observer()->OnRefreshTokenAvailable();
 
   token_save_callbacks_.push_back(std::move(callback));
@@ -151,7 +159,7 @@
 }
 
 void DeviceOAuth2TokenStoreChromeOS::OnServiceAccountIdentityChanged() {
-  if (!GetAccountId().empty() && !refresh_token_.empty())
+  if (observer() && !GetAccountId().empty() && !refresh_token_.empty())
     observer()->OnRefreshTokenAvailable();
 }
 
diff --git a/chrome/browser/device_identity/chromeos/device_oauth2_token_store_chromeos.h b/chrome/browser/device_identity/chromeos/device_oauth2_token_store_chromeos.h
index 8023447..ec23125 100644
--- a/chrome/browser/device_identity/chromeos/device_oauth2_token_store_chromeos.h
+++ b/chrome/browser/device_identity/chromeos/device_oauth2_token_store_chromeos.h
@@ -9,6 +9,8 @@
 
 #include "chrome/browser/chromeos/settings/cros_settings.h"
 
+class PrefRegistrySimple;
+
 namespace chromeos {
 
 // ChromeOS specific implementation of the DeviceOAuth2TokenStore interface used
@@ -25,6 +27,13 @@
   explicit DeviceOAuth2TokenStoreChromeOS(PrefService* local_state);
   ~DeviceOAuth2TokenStoreChromeOS() override;
 
+  DeviceOAuth2TokenStoreChromeOS(const DeviceOAuth2TokenStoreChromeOS& other) =
+      delete;
+  DeviceOAuth2TokenStoreChromeOS& operator=(
+      const DeviceOAuth2TokenStoreChromeOS& other) = delete;
+
+  static void RegisterPrefs(PrefRegistrySimple* registry);
+
   // DeviceOAuth2TokenStore:
   void Init(InitCallback callback) override;
   CoreAccountId GetAccountId() const override;
diff --git a/chrome/browser/device_identity/chromeos/device_oauth2_token_store_chromeos_unittest.cc b/chrome/browser/device_identity/chromeos/device_oauth2_token_store_chromeos_unittest.cc
new file mode 100644
index 0000000..f9eba7c
--- /dev/null
+++ b/chrome/browser/device_identity/chromeos/device_oauth2_token_store_chromeos_unittest.cc
@@ -0,0 +1,250 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/device_identity/chromeos/device_oauth2_token_store_chromeos.h"
+
+#include "base/run_loop.h"
+#include "base/task/thread_pool/thread_pool_instance.h"
+#include "base/test/bind_test_util.h"
+#include "chrome/browser/chromeos/policy/device_policy_builder.h"
+#include "chrome/browser/chromeos/settings/cros_settings.h"
+#include "chrome/browser/chromeos/settings/device_settings_service.h"
+#include "chrome/browser/chromeos/settings/scoped_testing_cros_settings.h"
+#include "chrome/browser/chromeos/settings/token_encryptor.h"
+#include "chrome/browser/device_identity/chromeos/device_oauth2_token_store_chromeos.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/base/scoped_testing_local_state.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chromeos/cryptohome/system_salt_getter.h"
+#include "chromeos/dbus/cryptohome/fake_cryptohome_client.h"
+#include "chromeos/dbus/session_manager/fake_session_manager_client.h"
+#include "chromeos/tpm/stub_install_attributes.h"
+#include "components/ownership/mock_owner_key_util.h"
+#include "content/public/test/browser_task_environment.h"
+#include "content/public/test/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+// Helper class for tests to wait until the store's init procedure is completed.
+class DeviceOAuth2TokenStoreInitWaiter {
+ public:
+  DeviceOAuth2TokenStoreInitWaiter() = default;
+  // The caller must ensure that the DeviceOAuth2TokenStoreInitWaiter outlives
+  // the callback it returns.
+  DeviceOAuth2TokenStore::InitCallback GetCallback() {
+    return base::BindOnce(&DeviceOAuth2TokenStoreInitWaiter::OnInit,
+                          base::Unretained(this));
+  }
+  void Wait() { run_loop_.Run(); }
+  bool HasInitBeenCalled() { return init_called_; }
+  bool GetInitResult() {
+    CHECK(init_called_);
+    return init_result_;
+  }
+  bool GetValidationRequired() {
+    CHECK(init_called_);
+    return validation_required_;
+  }
+
+ private:
+  void OnInit(bool init_result, bool validation_required) {
+    ASSERT_FALSE(init_called_);
+    init_called_ = true;
+    init_result_ = init_result;
+    validation_required_ = validation_required;
+    run_loop_.Quit();
+  }
+  base::RunLoop run_loop_;
+  bool init_called_ = false;
+  bool init_result_ = false;
+  bool validation_required_ = false;
+};
+}  // namespace
+
+class DeviceOAuth2TokenStoreChromeOSTest : public testing::Test {
+ public:
+  DeviceOAuth2TokenStoreChromeOSTest()
+      : scoped_testing_local_state_(TestingBrowserProcess::GetGlobal()) {}
+
+  void SetUp() override {
+    chromeos::CryptohomeClient::InitializeFake();
+    chromeos::FakeCryptohomeClient::Get()->SetServiceIsAvailable(true);
+    chromeos::FakeCryptohomeClient::Get()->set_system_salt(
+        chromeos::FakeCryptohomeClient::GetStubSystemSalt());
+
+    chromeos::SystemSaltGetter::Initialize();
+
+    scoped_refptr<ownership::MockOwnerKeyUtil> owner_key_util_(
+        new ownership::MockOwnerKeyUtil());
+    owner_key_util_->SetPublicKeyFromPrivateKey(
+        *device_policy_.GetSigningKey());
+    chromeos::DeviceSettingsService::Get()->SetSessionManager(
+        &session_manager_client_, owner_key_util_);
+  }
+
+  void TearDown() override {
+    base::ThreadPoolInstance::Get()->FlushForTesting();
+    chromeos::DeviceSettingsService::Get()->UnsetSessionManager();
+    chromeos::SystemSaltGetter::Shutdown();
+    chromeos::CryptohomeClient::Shutdown();
+  }
+
+  void SetUpDefaultValues() {
+    SetDeviceRefreshTokenInLocalState("device_refresh_token_4_test");
+    SetRobotAccountId("[email protected]");
+  }
+
+  void InitWithPendingSalt(chromeos::DeviceOAuth2TokenStoreChromeOS* store) {
+    chromeos::FakeCryptohomeClient::Get()->set_system_salt(
+        std::vector<uint8_t>());
+    chromeos::FakeCryptohomeClient::Get()->SetServiceIsAvailable(false);
+    store->Init(base::BindLambdaForTesting([](bool, bool) {}));
+    SetUpDefaultValues();
+  }
+
+  void InitStore(chromeos::DeviceOAuth2TokenStoreChromeOS* store) {
+    chromeos::FakeCryptohomeClient::Get()->set_system_salt(
+        std::vector<uint8_t>());
+    chromeos::FakeCryptohomeClient::Get()->SetServiceIsAvailable(false);
+
+    DeviceOAuth2TokenStoreInitWaiter init_waiter;
+    store->Init(init_waiter.GetCallback());
+
+    // Make the system salt available.
+    chromeos::FakeCryptohomeClient::Get()->set_system_salt(
+        chromeos::FakeCryptohomeClient::GetStubSystemSalt());
+    chromeos::FakeCryptohomeClient::Get()->SetServiceIsAvailable(true);
+
+    // Wait for init to complete before continuing with the test.
+    init_waiter.Wait();
+  }
+
+  void SetRobotAccountId(const std::string& account_id) {
+    device_policy_.policy_data().set_service_account_identity(account_id);
+    device_policy_.Build();
+    session_manager_client_.set_device_policy(device_policy_.GetBlob());
+    chromeos::DeviceSettingsService::Get()->Load();
+    content::RunAllTasksUntilIdle();
+  }
+
+  void SetDeviceRefreshTokenInLocalState(const std::string& refresh_token) {
+    scoped_testing_local_state_.Get()->SetUserPref(
+        prefs::kDeviceRobotAnyApiRefreshToken,
+        std::make_unique<base::Value>(refresh_token));
+  }
+
+  content::BrowserTaskEnvironment task_environment_;
+  ScopedTestingLocalState scoped_testing_local_state_;
+  chromeos::ScopedStubInstallAttributes scoped_stub_install_attributes_;
+  chromeos::ScopedTestDeviceSettingsService scoped_device_settings_service_;
+  chromeos::ScopedTestCrosSettings scoped_test_cros_settings_{
+      scoped_testing_local_state_.Get()};
+  chromeos::FakeSessionManagerClient session_manager_client_;
+  policy::DevicePolicyBuilder device_policy_;
+};
+
+TEST_F(DeviceOAuth2TokenStoreChromeOSTest, InitSuccessful) {
+  chromeos::FakeCryptohomeClient::Get()->set_system_salt(
+      std::vector<uint8_t>());
+  chromeos::FakeCryptohomeClient::Get()->SetServiceIsAvailable(false);
+
+  chromeos::DeviceOAuth2TokenStoreChromeOS store(
+      scoped_testing_local_state_.Get());
+
+  EXPECT_TRUE(store.GetAccountId().empty());
+  EXPECT_TRUE(store.GetRefreshToken().empty());
+
+  DeviceOAuth2TokenStoreInitWaiter init_waiter;
+  store.Init(init_waiter.GetCallback());
+
+  EXPECT_FALSE(init_waiter.HasInitBeenCalled());
+
+  // Make the system salt available.
+  chromeos::FakeCryptohomeClient::Get()->set_system_salt(
+      chromeos::FakeCryptohomeClient::GetStubSystemSalt());
+  chromeos::FakeCryptohomeClient::Get()->SetServiceIsAvailable(true);
+  init_waiter.Wait();
+
+  EXPECT_TRUE(init_waiter.HasInitBeenCalled());
+  EXPECT_TRUE(init_waiter.GetInitResult());
+  EXPECT_TRUE(init_waiter.GetValidationRequired());
+}
+
+TEST_F(DeviceOAuth2TokenStoreChromeOSTest, SaveToken) {
+  chromeos::DeviceOAuth2TokenStoreChromeOS store(
+      scoped_testing_local_state_.Get());
+
+  InitStore(&store);
+
+  store.SetAndSaveRefreshToken("test-token",
+                               DeviceOAuth2TokenStore::StatusCallback());
+  EXPECT_EQ("test-token", store.GetRefreshToken());
+}
+
+TEST_F(DeviceOAuth2TokenStoreChromeOSTest, SaveEncryptedTokenEarly) {
+  chromeos::DeviceOAuth2TokenStoreChromeOS store(
+      scoped_testing_local_state_.Get());
+
+  // Set a new refresh token without the system salt available.
+  InitWithPendingSalt(&store);
+
+  store.SetAndSaveRefreshToken("test-token",
+                               DeviceOAuth2TokenStore::StatusCallback());
+  EXPECT_EQ("test-token", store.GetRefreshToken());
+
+  // Make the system salt available.
+  chromeos::FakeCryptohomeClient::Get()->set_system_salt(
+      chromeos::FakeCryptohomeClient::GetStubSystemSalt());
+  chromeos::FakeCryptohomeClient::Get()->SetServiceIsAvailable(true);
+  base::RunLoop().RunUntilIdle();
+
+  // The original token should still be present.
+  EXPECT_EQ("test-token", store.GetRefreshToken());
+
+  // Reloading shouldn't change the token either.
+  chromeos::DeviceOAuth2TokenStoreChromeOS other_store(
+      scoped_testing_local_state_.Get());
+  InitStore(&other_store);
+
+  EXPECT_EQ("test-token", other_store.GetRefreshToken());
+}
+
+TEST_F(DeviceOAuth2TokenStoreChromeOSTest, DoNotAnnounceTokenWithoutAccountID) {
+  chromeos::DeviceOAuth2TokenStoreChromeOS store(
+      scoped_testing_local_state_.Get());
+  InitStore(&store);
+
+  class StoreObserver : public DeviceOAuth2TokenStore::Observer {
+   public:
+    using Callback = base::Callback<void()>;
+    explicit StoreObserver(Callback callback) : callback_(callback) {}
+
+    void OnRefreshTokenAvailable() override { callback_.Run(); }
+
+    Callback callback_;
+  };
+
+  auto callback_that_should_not_be_called =
+      base::BindRepeating([]() { FAIL(); });
+  StoreObserver observer_not_called(
+      std::move(callback_that_should_not_be_called));
+  store.SetObserver(&observer_not_called);
+
+  // Make a token available during enrollment. Verify that the token is not
+  // announced yet.
+  store.SetAndSaveRefreshToken("test-token",
+                               DeviceOAuth2TokenStore::StatusCallback());
+
+  base::RunLoop run_loop;
+  auto callback_that_should_be_called_once =
+      base::BindRepeating([](base::RunLoop* loop) { loop->Quit(); }, &run_loop);
+  StoreObserver observer_called_once(
+      std::move(callback_that_should_be_called_once));
+  store.SetObserver(&observer_called_once);
+
+  // Also make the robot account ID available. Verify that the token is
+  // announced now.
+  SetRobotAccountId("[email protected]");
+  run_loop.Run();
+}
diff --git a/chrome/browser/device_identity/device_identity_provider.cc b/chrome/browser/device_identity/device_identity_provider.cc
index 34972082..dfed68a 100644
--- a/chrome/browser/device_identity/device_identity_provider.cc
+++ b/chrome/browser/device_identity/device_identity_provider.cc
@@ -7,8 +7,6 @@
 #include "base/bind_helpers.h"
 #include "chrome/browser/device_identity/device_oauth2_token_service.h"
 
-namespace chromeos {
-
 namespace {
 
 // An implementation of ActiveAccountAccessTokenFetcher that is backed by
@@ -83,7 +81,7 @@
 }
 
 DeviceIdentityProvider::DeviceIdentityProvider(
-    chromeos::DeviceOAuth2TokenService* token_service)
+    DeviceOAuth2TokenService* token_service)
     : token_service_(token_service) {
   // TODO(blundell): Can |token_service_| ever actually be non-null?
   if (token_service_) {
@@ -147,5 +145,3 @@
 void DeviceIdentityProvider::OnRefreshTokenAvailable() {
   ProcessRefreshTokenUpdateForAccount(GetActiveAccountId());
 }
-
-}  // namespace chromeos
diff --git a/chrome/browser/device_identity/device_identity_provider.h b/chrome/browser/device_identity/device_identity_provider.h
index b08fc9b..4660cbe 100644
--- a/chrome/browser/device_identity/device_identity_provider.h
+++ b/chrome/browser/device_identity/device_identity_provider.h
@@ -8,15 +8,12 @@
 #include "base/macros.h"
 #include "components/invalidation/public/identity_provider.h"
 
-namespace chromeos {
-
 class DeviceOAuth2TokenService;
 
 // Identity provider implementation backed by DeviceOAuth2TokenService.
 class DeviceIdentityProvider : public invalidation::IdentityProvider {
  public:
-  explicit DeviceIdentityProvider(
-      chromeos::DeviceOAuth2TokenService* token_service);
+  explicit DeviceIdentityProvider(DeviceOAuth2TokenService* token_service);
   ~DeviceIdentityProvider() override;
 
   // IdentityProvider:
@@ -34,11 +31,9 @@
  private:
   void OnRefreshTokenAvailable();
 
-  chromeos::DeviceOAuth2TokenService* token_service_;
+  DeviceOAuth2TokenService* token_service_;
 
   DISALLOW_COPY_AND_ASSIGN(DeviceIdentityProvider);
 };
 
-}  // namespace chromeos
-
 #endif  // CHROME_BROWSER_DEVICE_IDENTITY_DEVICE_IDENTITY_PROVIDER_H_
diff --git a/chrome/browser/device_identity/device_oauth2_token_service.cc b/chrome/browser/device_identity/device_oauth2_token_service.cc
index b16712b..974dbe7 100644
--- a/chrome/browser/device_identity/device_oauth2_token_service.cc
+++ b/chrome/browser/device_identity/device_oauth2_token_service.cc
@@ -15,9 +15,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
 #include "chrome/browser/device_identity/device_oauth2_token_store.h"
-#include "chrome/common/pref_names.h"
 #include "components/policy/proto/device_management_backend.pb.h"
-#include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 #include "google_apis/gaia/gaia_constants.h"
 #include "google_apis/gaia/gaia_urls.h"
@@ -27,8 +25,6 @@
 #include "google_apis/gaia/oauth2_access_token_fetcher_impl.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
-namespace chromeos {
-
 struct DeviceOAuth2TokenService::PendingRequest {
   PendingRequest(
       const base::WeakPtr<OAuth2AccessTokenManager::RequestImpl>& request,
@@ -65,12 +61,6 @@
   FlushPendingRequests(false, GoogleServiceAuthError::REQUEST_CANCELED);
 }
 
-// static
-void DeviceOAuth2TokenService::RegisterPrefs(PrefRegistrySimple* registry) {
-  registry->RegisterStringPref(prefs::kDeviceRobotAnyApiRefreshToken,
-                               std::string());
-}
-
 void DeviceOAuth2TokenService::SetAndSaveRefreshToken(
     const std::string& refresh_token,
     const StatusCallback& result_callback) {
@@ -137,6 +127,13 @@
   return token_manager_.get();
 }
 
+#if !defined(OS_CHROMEOS)
+void DeviceOAuth2TokenService::SetServiceAccountEmail(
+    const std::string& account_email) {
+  store_->SetAccountEmail(account_email);
+}
+#endif
+
 void DeviceOAuth2TokenService::OnRefreshTokenResponse(
     const std::string& access_token,
     int expires_in_seconds) {
@@ -385,5 +382,3 @@
   else
     FlushPendingRequests(false, error);
 }
-
-}  // namespace chromeos
diff --git a/chrome/browser/device_identity/device_oauth2_token_service.h b/chrome/browser/device_identity/device_oauth2_token_service.h
index 15f1d19..60544ace 100644
--- a/chrome/browser/device_identity/device_oauth2_token_service.h
+++ b/chrome/browser/device_identity/device_oauth2_token_service.h
@@ -23,9 +23,6 @@
 
 class OAuth2AccessTokenFetcher;
 class OAuth2AccessTokenConsumer;
-class PrefRegistrySimple;
-
-namespace chromeos {
 
 // DeviceOAuth2TokenService retrieves OAuth2 access tokens for a given
 // set of scopes using the device-level OAuth2 any-api refresh token
@@ -45,8 +42,6 @@
   void SetAndSaveRefreshToken(const std::string& refresh_token,
                               const StatusCallback& callback);
 
-  static void RegisterPrefs(PrefRegistrySimple* registry);
-
   // Pull the robot account ID from device policy.
   CoreAccountId GetRobotAccountId() const;
 
@@ -79,6 +74,13 @@
 
   OAuth2AccessTokenManager* GetAccessTokenManager();
 
+#if !defined(OS_CHROMEOS)
+  // Used on non-ChromeOS platforms to set the email associated with the
+  // current service account. On ChromeOS, this function isn't used because
+  // the service account identity comes from CrosSettings.
+  void SetServiceAccountEmail(const std::string& account_email);
+#endif
+
   // Can be used to override the robot account ID for testing purposes. Most
   // common use case is to easily inject a non-empty account ID to make the
   // refresh token for the robot account visible via GetAccounts() and
@@ -194,6 +196,4 @@
   DISALLOW_COPY_AND_ASSIGN(DeviceOAuth2TokenService);
 };
 
-}  // namespace chromeos
-
 #endif  // CHROME_BROWSER_DEVICE_IDENTITY_DEVICE_OAUTH2_TOKEN_SERVICE_H_
diff --git a/chrome/browser/device_identity/device_oauth2_token_service_factory.cc b/chrome/browser/device_identity/device_oauth2_token_service_factory.cc
index 1d4a6c83..a14ba71 100644
--- a/chrome/browser/device_identity/device_oauth2_token_service_factory.cc
+++ b/chrome/browser/device_identity/device_oauth2_token_service_factory.cc
@@ -6,19 +6,37 @@
 
 #include <memory>
 
+#include "build/build_config.h"
 #include "chrome/browser/chromeos/settings/token_encryptor.h"
+#if defined(OS_CHROMEOS)
 #include "chrome/browser/device_identity/chromeos/device_oauth2_token_store_chromeos.h"
+#else
+#include "chrome/browser/device_identity/device_oauth2_token_store_desktop.h"
+#endif
 #include "chrome/browser/device_identity/device_oauth2_token_service.h"
 #include "chromeos/cryptohome/system_salt_getter.h"
+#include "components/policy/core/common/features.h"
 #include "content/public/browser/browser_thread.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
-namespace chromeos {
-
 namespace {
 
 static DeviceOAuth2TokenService* g_device_oauth2_token_service_ = nullptr;
 
+std::unique_ptr<DeviceOAuth2TokenStore> CreatePlatformTokenStore(
+    PrefService* local_state) {
+#if defined(OS_CHROMEOS)
+  return std::make_unique<chromeos::DeviceOAuth2TokenStoreChromeOS>(
+      local_state);
+#elif defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX)
+  DCHECK(base::FeatureList::IsEnabled(policy::features::kCBCMServiceAccounts));
+  return std::make_unique<DeviceOAuth2TokenStoreDesktop>(local_state);
+#else
+  NOTREACHED();
+  return nullptr;
+#endif
+}
+
 }  // namespace
 
 // static
@@ -34,8 +52,7 @@
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   DCHECK(!g_device_oauth2_token_service_);
   g_device_oauth2_token_service_ = new DeviceOAuth2TokenService(
-      url_loader_factory, std::unique_ptr<DeviceOAuth2TokenStore>(
-                              new DeviceOAuth2TokenStoreChromeOS(local_state)));
+      url_loader_factory, CreatePlatformTokenStore(local_state));
 }
 
 // static
@@ -46,5 +63,3 @@
     g_device_oauth2_token_service_ = nullptr;
   }
 }
-
-}  // namespace chromeos
diff --git a/chrome/browser/device_identity/device_oauth2_token_service_factory.h b/chrome/browser/device_identity/device_oauth2_token_service_factory.h
index 9dba99d..2b1c1731 100644
--- a/chrome/browser/device_identity/device_oauth2_token_service_factory.h
+++ b/chrome/browser/device_identity/device_oauth2_token_service_factory.h
@@ -17,8 +17,6 @@
 class SharedURLLoaderFactory;
 }
 
-namespace chromeos {
-
 class DeviceOAuth2TokenService;
 
 class DeviceOAuth2TokenServiceFactory {
@@ -51,6 +49,4 @@
   DISALLOW_COPY_AND_ASSIGN(DeviceOAuth2TokenServiceFactory);
 };
 
-}  // namespace chromeos
-
 #endif  // CHROME_BROWSER_DEVICE_IDENTITY_DEVICE_OAUTH2_TOKEN_SERVICE_FACTORY_H_
diff --git a/chrome/browser/device_identity/device_oauth2_token_service_unittest.cc b/chrome/browser/device_identity/device_oauth2_token_service_unittest.cc
index 9087d92..90860b2 100644
--- a/chrome/browser/device_identity/device_oauth2_token_service_unittest.cc
+++ b/chrome/browser/device_identity/device_oauth2_token_service_unittest.cc
@@ -13,20 +13,9 @@
 #include "base/run_loop.h"
 #include "base/task/thread_pool/thread_pool_instance.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "chrome/browser/chromeos/policy/device_policy_builder.h"
-#include "chrome/browser/chromeos/settings/cros_settings.h"
-#include "chrome/browser/chromeos/settings/device_settings_service.h"
-#include "chrome/browser/chromeos/settings/scoped_testing_cros_settings.h"
-#include "chrome/browser/chromeos/settings/token_encryptor.h"
-#include "chrome/browser/device_identity/chromeos/device_oauth2_token_store_chromeos.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/scoped_testing_local_state.h"
 #include "chrome/test/base/testing_browser_process.h"
-#include "chromeos/cryptohome/system_salt_getter.h"
-#include "chromeos/dbus/cryptohome/fake_cryptohome_client.h"
-#include "chromeos/dbus/session_manager/fake_session_manager_client.h"
-#include "chromeos/tpm/stub_install_attributes.h"
-#include "components/ownership/mock_owner_key_util.h"
 #include "components/prefs/testing_pref_service.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/test/browser_task_environment.h"
@@ -41,7 +30,64 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace chromeos {
+class MockDeviceOAuth2TokenStore : public DeviceOAuth2TokenStore {
+ public:
+  MockDeviceOAuth2TokenStore() = default;
+  ~MockDeviceOAuth2TokenStore() override = default;
+
+  // DeviceOAuth2TokenStore:
+  void Init(InitCallback callback) override {
+    pending_init_callback_ = std::move(callback);
+  }
+  CoreAccountId GetAccountId() const override { return account_id_; }
+  std::string GetRefreshToken() const override { return refresh_token_; }
+
+  void SetAndSaveRefreshToken(const std::string& refresh_token,
+                              StatusCallback result_callback) override {
+    refresh_token_ = refresh_token;
+    pending_status_callback_ = std::move(result_callback);
+  }
+
+  void PrepareTrustedAccountId(TrustedAccountIdCallback callback) override {
+    pending_trusted_account_id_callback_ = std::move(callback);
+    TriggerTrustedAccountIdCallback(true);
+  }
+
+#if !defined(OS_CHROMEOS)
+  void SetAccountEmail(const std::string& account_email) override {
+    account_id_ = CoreAccountId::FromEmail(account_email);
+  }
+#endif
+
+  // Mock-specific functions:
+  void SetRefreshTokenForTesting(const std::string& token) {
+    refresh_token_ = token;
+  }
+
+  void SetAccountIdForTesting(CoreAccountId account_id) {
+    account_id_ = account_id;
+  }
+
+  void TriggerInitCallback(bool success, bool validation_required) {
+    std::move(pending_init_callback_).Run(success, validation_required);
+  }
+
+  void TriggerStatusCallback(bool success) {
+    std::move(pending_status_callback_).Run(success);
+  }
+
+  void TriggerTrustedAccountIdCallback(bool account_present) {
+    std::move(pending_trusted_account_id_callback_).Run(account_present);
+  }
+
+ private:
+  CoreAccountId account_id_;
+  std::string refresh_token_;
+
+  InitCallback pending_init_callback_;
+  StatusCallback pending_status_callback_;
+  TrustedAccountIdCallback pending_trusted_account_id_callback_;
+};
 
 class DeviceOAuth2TokenServiceTest : public testing::Test {
  public:
@@ -51,26 +97,16 @@
   // Most tests just want a noop crypto impl with a dummy refresh token value in
   // Local State (if the value is an empty string, it will be ignored).
   void SetUpDefaultValues() {
-    SetDeviceRefreshTokenInLocalState("device_refresh_token_4_test");
-    SetRobotAccountId("[email protected]");
     CreateService();
+    token_store_->SetRefreshTokenForTesting("device_refresh_token_4_test");
+    SetRobotAccountId("[email protected]");
     AssertConsumerTokensAndErrors(0, 0);
 
-    base::RunLoop().RunUntilIdle();
-  }
-
-  void SetUpWithPendingSalt() {
-    FakeCryptohomeClient::Get()->set_system_salt(std::vector<uint8_t>());
-    FakeCryptohomeClient::Get()->SetServiceIsAvailable(false);
-    SetUpDefaultValues();
+    token_store_->TriggerInitCallback(true, true);
   }
 
   void SetRobotAccountId(const std::string& account_id) {
-    device_policy_.policy_data().set_service_account_identity(account_id);
-    device_policy_.Build();
-    session_manager_client_.set_device_policy(device_policy_.GetBlob());
-    DeviceSettingsService::Get()->Load();
-    content::RunAllTasksUntilIdle();
+    token_store_->SetAccountIdForTesting(CoreAccountId::FromEmail(account_id));
   }
 
   std::unique_ptr<OAuth2AccessTokenManager::Request> StartTokenRequest() {
@@ -78,50 +114,27 @@
                                                     &consumer_);
   }
 
-  void SetUp() override {
-    CryptohomeClient::InitializeFake();
-    FakeCryptohomeClient::Get()->SetServiceIsAvailable(true);
-    FakeCryptohomeClient::Get()->set_system_salt(
-        FakeCryptohomeClient::GetStubSystemSalt());
-
-    SystemSaltGetter::Initialize();
-
-    scoped_refptr<ownership::MockOwnerKeyUtil> owner_key_util_(
-        new ownership::MockOwnerKeyUtil());
-    owner_key_util_->SetPublicKeyFromPrivateKey(
-        *device_policy_.GetSigningKey());
-    DeviceSettingsService::Get()->SetSessionManager(&session_manager_client_,
-                                                    owner_key_util_);
-  }
+  void SetUp() override {}
 
   void TearDown() override {
     oauth2_service_.reset();
     base::ThreadPoolInstance::Get()->FlushForTesting();
-    DeviceSettingsService::Get()->UnsetSessionManager();
-    SystemSaltGetter::Shutdown();
-    CryptohomeClient::Shutdown();
     base::RunLoop().RunUntilIdle();
   }
 
+  MockDeviceOAuth2TokenStore* token_store() { return token_store_; }
+
   void CreateService() {
+    auto store = std::make_unique<MockDeviceOAuth2TokenStore>();
+    token_store_ = store.get();
+
     oauth2_service_.reset(new DeviceOAuth2TokenService(
-        test_url_loader_factory_.GetSafeWeakWrapper(),
-        std::unique_ptr<DeviceOAuth2TokenStore>(
-            new DeviceOAuth2TokenStoreChromeOS(
-                scoped_testing_local_state_.Get()))));
+        test_url_loader_factory_.GetSafeWeakWrapper(), std::move(store)));
     oauth2_service_->max_refresh_token_validation_retries_ = 0;
     oauth2_service_->GetAccessTokenManager()
         ->set_max_authorization_token_fetch_retries_for_testing(0);
   }
 
-  // Utility method to set a value in Local State for the device refresh token
-  // (it must have a non-empty value or it won't be used).
-  void SetDeviceRefreshTokenInLocalState(const std::string& refresh_token) {
-    scoped_testing_local_state_.Get()->SetUserPref(
-        prefs::kDeviceRobotAnyApiRefreshToken,
-        std::make_unique<base::Value>(refresh_token));
-  }
-
   std::string GetValidTokenInfoResponse(const std::string& email) {
     return "{ \"email\": \"" + email +
            "\","
@@ -135,7 +148,6 @@
   std::string GetRefreshToken() {
     if (!RefreshTokenIsAvailable())
       return std::string();
-
     return oauth2_service_->GetRefreshToken();
   }
 
@@ -175,17 +187,12 @@
   };
 
   content::BrowserTaskEnvironment task_environment_;
-  ScopedStubInstallAttributes scoped_stub_install_attributes_;
   ScopedTestingLocalState scoped_testing_local_state_;
-  ScopedTestDeviceSettingsService scoped_device_settings_service_;
-  ScopedTestCrosSettings scoped_test_cros_settings_{
-      scoped_testing_local_state_.Get()};
   network::TestURLLoaderFactory test_url_loader_factory_;
-  FakeSessionManagerClient session_manager_client_;
-  policy::DevicePolicyBuilder device_policy_;
   std::unique_ptr<DeviceOAuth2TokenService, TokenServiceDeleter>
       oauth2_service_;
   TestingOAuth2AccessTokenManagerConsumer consumer_;
+  MockDeviceOAuth2TokenStore* token_store_;
 };
 
 void DeviceOAuth2TokenServiceTest::ReturnOAuthUrlFetchResults(
@@ -233,41 +240,6 @@
   EXPECT_EQ(num_errors, consumer_.number_of_errors_);
 }
 
-TEST_F(DeviceOAuth2TokenServiceTest, SaveEncryptedToken) {
-  CreateService();
-
-  // The token service won't report there being a token if the robot account ID
-  // is not set, which would cause the expectation below to fail.
-  SetRobotAccountId("[email protected]");
-
-  oauth2_service_->SetAndSaveRefreshToken(
-      "test-token", DeviceOAuth2TokenService::StatusCallback());
-  EXPECT_EQ("test-token", GetRefreshToken());
-}
-
-TEST_F(DeviceOAuth2TokenServiceTest, SaveEncryptedTokenEarly) {
-  // Set a new refresh token without the system salt available.
-  SetUpWithPendingSalt();
-
-  oauth2_service_->SetAndSaveRefreshToken(
-      "test-token", DeviceOAuth2TokenService::StatusCallback());
-  EXPECT_EQ("test-token", GetRefreshToken());
-
-  // Make the system salt available.
-  FakeCryptohomeClient::Get()->set_system_salt(
-      FakeCryptohomeClient::GetStubSystemSalt());
-  FakeCryptohomeClient::Get()->SetServiceIsAvailable(true);
-  base::RunLoop().RunUntilIdle();
-
-  // The original token should still be present.
-  EXPECT_EQ("test-token", GetRefreshToken());
-
-  // Reloading shouldn't change the token either.
-  CreateService();
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ("test-token", GetRefreshToken());
-}
-
 TEST_F(DeviceOAuth2TokenServiceTest, RefreshTokenValidation_Success) {
   SetUpDefaultValues();
   std::unique_ptr<OAuth2AccessTokenManager::Request> request =
@@ -280,16 +252,16 @@
 }
 
 TEST_F(DeviceOAuth2TokenServiceTest, RefreshTokenValidation_SuccessAsyncLoad) {
-  SetUpWithPendingSalt();
+  CreateService();
+  token_store()->SetRefreshTokenForTesting("device_refresh_token_4_test");
+  SetRobotAccountId("[email protected]");
 
   std::unique_ptr<OAuth2AccessTokenManager::Request> request =
       StartTokenRequest();
   PerformURLFetches();
   AssertConsumerTokensAndErrors(0, 0);
 
-  FakeCryptohomeClient::Get()->set_system_salt(
-      FakeCryptohomeClient::GetStubSystemSalt());
-  FakeCryptohomeClient::Get()->SetServiceIsAvailable(true);
+  token_store()->TriggerInitCallback(true, true);
   base::RunLoop().RunUntilIdle();
 
   PerformURLFetches();
@@ -309,10 +281,11 @@
   // Test succeeds if this line is reached without a crash.
 }
 
-TEST_F(DeviceOAuth2TokenServiceTest, RefreshTokenValidation_NoSalt) {
-  FakeCryptohomeClient::Get()->set_system_salt(std::vector<uint8_t>());
-  FakeCryptohomeClient::Get()->SetServiceIsAvailable(true);
-  SetUpDefaultValues();
+TEST_F(DeviceOAuth2TokenServiceTest, RefreshTokenValidation_InitFailure) {
+  CreateService();
+  token_store()->SetRefreshTokenForTesting("device_refresh_token_4_test");
+  SetRobotAccountId("[email protected]");
+  token_store()->TriggerInitCallback(false, true);
 
   EXPECT_FALSE(RefreshTokenIsAvailable());
 
@@ -439,30 +412,3 @@
   PerformURLFetches();
   AssertConsumerTokensAndErrors(1, 1);
 }
-
-TEST_F(DeviceOAuth2TokenServiceTest, DoNotAnnounceTokenWithoutAccountID) {
-  CreateService();
-
-  auto callback_that_should_not_be_called =
-      base::BindRepeating([]() { FAIL(); });
-  oauth2_service_->SetRefreshTokenAvailableCallback(
-      std::move(callback_that_should_not_be_called));
-
-  // Make a token available during enrollment. Verify that the token is not
-  // announced yet.
-  oauth2_service_->SetAndSaveRefreshToken(
-      "test-token", DeviceOAuth2TokenService::StatusCallback());
-
-  base::RunLoop run_loop;
-  auto callback_that_should_be_called_once =
-      base::BindRepeating([](base::RunLoop* loop) { loop->Quit(); }, &run_loop);
-  oauth2_service_->SetRefreshTokenAvailableCallback(
-      std::move(callback_that_should_be_called_once));
-
-  // Also make the robot account ID available. Verify that the token is
-  // announced now.
-  SetRobotAccountId("[email protected]");
-  run_loop.Run();
-}
-
-}  // namespace chromeos
diff --git a/chrome/browser/device_identity/device_oauth2_token_store.h b/chrome/browser/device_identity/device_oauth2_token_store.h
index 20ea339e..54ee49e 100644
--- a/chrome/browser/device_identity/device_oauth2_token_store.h
+++ b/chrome/browser/device_identity/device_oauth2_token_store.h
@@ -73,6 +73,14 @@
   // Invokes |callback| when the operation completes.
   virtual void PrepareTrustedAccountId(TrustedAccountIdCallback callback) = 0;
 
+#if !defined(OS_CHROMEOS)
+  // Requests that this store persist the current service account's associated
+  // email.
+  // On ChromeOS, the account email comes from CrosSettings so this should never
+  // be called.
+  virtual void SetAccountEmail(const std::string& account_email) = 0;
+#endif
+
   void SetObserver(Observer* observer) { observer_ = observer; }
   Observer* observer() { return observer_; }
 
diff --git a/chrome/browser/device_identity/device_oauth2_token_store_desktop.cc b/chrome/browser/device_identity/device_oauth2_token_store_desktop.cc
new file mode 100644
index 0000000..471c77c
--- /dev/null
+++ b/chrome/browser/device_identity/device_oauth2_token_store_desktop.cc
@@ -0,0 +1,118 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/device_identity/device_oauth2_token_store_desktop.h"
+
+#include <string>
+
+#include "base/base64.h"
+#include "base/bind.h"
+#include "chrome/common/pref_names.h"
+#include "components/os_crypt/os_crypt.h"
+#include "components/policy/core/common/policy_pref_names.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
+
+// This pref will hold the base64-encoded representation of the encrypted
+// refresh token for the browser's service account.
+const char kCBCMServiceAccountRefreshToken[] =
+    "cbcm.service_account_refresh_token";
+
+// The account email for the robot account used for policy invalidations on
+// Desktop platforms by Chrome Browser Cloud Management (CBCM). This is similar
+// to kDeviceRobotAnyApiRefreshToken on ChromeOS.
+const char kCBCMServiceAccountEmail[] = "cbcm.service_account_email";
+
+DeviceOAuth2TokenStoreDesktop::DeviceOAuth2TokenStoreDesktop(
+    PrefService* local_state)
+    : local_state_(local_state) {}
+DeviceOAuth2TokenStoreDesktop::~DeviceOAuth2TokenStoreDesktop() = default;
+
+// static
+void DeviceOAuth2TokenStoreDesktop::RegisterPrefs(
+    PrefRegistrySimple* registry) {
+  registry->RegisterStringPref(kCBCMServiceAccountRefreshToken, std::string());
+  registry->RegisterStringPref(kCBCMServiceAccountEmail, std::string());
+}
+
+void DeviceOAuth2TokenStoreDesktop::Init(InitCallback callback) {
+  std::string base64_encrypted_token =
+      local_state_->GetString(kCBCMServiceAccountRefreshToken);
+
+  if (base64_encrypted_token.empty()) {
+    // It's valid for the refresh token to not exist in the store, in
+    // which case init is successful and there shouldn't be a token
+    // validation step.
+    std::move(callback).Run(true, false);
+    return;
+  }
+
+  std::string decoded;
+  if (!base::Base64Decode(base64_encrypted_token, &decoded)) {
+    std::move(callback).Run(false, true);
+    return;
+  }
+
+  std::string decrypted_token;
+  bool success = OSCrypt::DecryptString(decoded, &decrypted_token);
+  if (success) {
+    refresh_token_ = decrypted_token;
+    // If the robot account ID is not available yet, do not announce the token.
+    // It will be done from OnServiceAccountIdentityChanged() once the robot
+    // account ID becomes available as well.
+    if (observer() && !GetAccountId().empty())
+      observer()->OnRefreshTokenAvailable();
+  }
+
+  std::move(callback).Run(success, true);
+}
+
+CoreAccountId DeviceOAuth2TokenStoreDesktop::GetAccountId() const {
+  return CoreAccountId::FromEmail(
+      local_state_->GetString(kCBCMServiceAccountEmail));
+}
+
+std::string DeviceOAuth2TokenStoreDesktop::GetRefreshToken() const {
+  return refresh_token_;
+}
+
+void DeviceOAuth2TokenStoreDesktop::SetAndSaveRefreshToken(
+    const std::string& refresh_token,
+    StatusCallback result_callback) {
+  std::string encrypted_token;
+  bool success = OSCrypt::EncryptString(refresh_token, &encrypted_token);
+
+  if (success) {
+    refresh_token_ = refresh_token;
+
+    // The string must be encoded as base64 for storage in local state.
+    std::string encoded;
+    base::Base64Encode(encrypted_token, &encoded);
+
+    local_state_->SetString(kCBCMServiceAccountRefreshToken, encoded);
+  }
+
+  std::move(result_callback).Run(success);
+}
+
+void DeviceOAuth2TokenStoreDesktop::PrepareTrustedAccountId(
+    TrustedAccountIdCallback callback) {
+  // There's no cryptohome or anything similar to initialize on non-chromeos
+  // platforms, so just run the callback as a success now.
+  callback.Run(true);
+}
+
+void DeviceOAuth2TokenStoreDesktop::SetAccountEmail(
+    const std::string& account_email) {
+  if (GetAccountId() == CoreAccountId::FromEmail(account_email))
+    return;
+
+  local_state_->SetString(kCBCMServiceAccountEmail, account_email);
+  OnServiceAccountIdentityChanged();
+}
+
+void DeviceOAuth2TokenStoreDesktop::OnServiceAccountIdentityChanged() {
+  if (observer() && !GetAccountId().empty() && !refresh_token_.empty())
+    observer()->OnRefreshTokenAvailable();
+}
diff --git a/chrome/browser/device_identity/device_oauth2_token_store_desktop.h b/chrome/browser/device_identity/device_oauth2_token_store_desktop.h
new file mode 100644
index 0000000..835a3dff
--- /dev/null
+++ b/chrome/browser/device_identity/device_oauth2_token_store_desktop.h
@@ -0,0 +1,56 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_DEVICE_IDENTITY_DEVICE_OAUTH2_TOKEN_STORE_DESKTOP_H_
+#define CHROME_BROWSER_DEVICE_IDENTITY_DEVICE_OAUTH2_TOKEN_STORE_DESKTOP_H_
+
+#include <string>
+
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/device_identity/device_oauth2_token_store.h"
+#include "google_apis/gaia/core_account_id.h"
+
+class PrefRegistrySimple;
+class PrefService;
+
+// The pref name where this class stores the encrypted refresh token.
+extern const char kCBCMServiceAccountRefreshToken[];
+
+// The pref name where this class stores the service account's email.
+extern const char kCBCMServiceAccountEmail[];
+
+// The desktop (mac, win, linux) implementation of DeviceOAuth2TokenStore. This
+// is used by the DeviceOAuth2TokenService on those platforms to encrypt and
+// persist the refresh token of the service account to LocalState.
+class DeviceOAuth2TokenStoreDesktop : public DeviceOAuth2TokenStore {
+ public:
+  explicit DeviceOAuth2TokenStoreDesktop(PrefService* local_state);
+  ~DeviceOAuth2TokenStoreDesktop() override;
+
+  DeviceOAuth2TokenStoreDesktop(const DeviceOAuth2TokenStoreDesktop& other) =
+      delete;
+  DeviceOAuth2TokenStoreDesktop& operator=(
+      const DeviceOAuth2TokenStoreDesktop& other) = delete;
+
+  static void RegisterPrefs(PrefRegistrySimple* registry);
+
+  // DeviceOAuth2TokenStore:
+  void Init(InitCallback callback) override;
+  CoreAccountId GetAccountId() const override;
+  std::string GetRefreshToken() const override;
+  void SetAndSaveRefreshToken(const std::string& refresh_token,
+                              StatusCallback result_callback) override;
+  void PrepareTrustedAccountId(TrustedAccountIdCallback callback) override;
+  void SetAccountEmail(const std::string& account_email) override;
+
+ private:
+  void OnServiceAccountIdentityChanged();
+
+  PrefService* const local_state_;
+  std::string refresh_token_;
+
+  base::WeakPtrFactory<DeviceOAuth2TokenStoreDesktop> weak_ptr_factory_{this};
+};
+
+#endif  // CHROME_BROWSER_DEVICE_IDENTITY_DEVICE_OAUTH2_TOKEN_STORE_DESKTOP_H_
diff --git a/chrome/browser/device_identity/device_oauth2_token_store_desktop_unittest.cc b/chrome/browser/device_identity/device_oauth2_token_store_desktop_unittest.cc
new file mode 100644
index 0000000..a4643a9
--- /dev/null
+++ b/chrome/browser/device_identity/device_oauth2_token_store_desktop_unittest.cc
@@ -0,0 +1,158 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/device_identity/device_oauth2_token_store_desktop.h"
+
+#include "base/base64.h"
+#include "base/test/bind_test_util.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/base/scoped_testing_local_state.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "components/os_crypt/os_crypt.h"
+#include "components/os_crypt/os_crypt_mocker.h"
+#include "components/policy/core/common/policy_pref_names.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+class TestObserver : public DeviceOAuth2TokenStore::Observer {
+ public:
+  int called_count() const { return called_count_; }
+
+ private:
+  void OnRefreshTokenAvailable() override { ++called_count_; }
+
+  int called_count_ = 0;
+};
+}  // namespace
+
+class DeviceOAuth2TokenStoreDesktopTest : public testing::Test {
+ public:
+  DeviceOAuth2TokenStoreDesktopTest()
+      : scoped_testing_local_state_(TestingBrowserProcess::GetGlobal()) {}
+  ~DeviceOAuth2TokenStoreDesktopTest() override = default;
+
+  ScopedTestingLocalState* scoped_testing_local_state() {
+    return &scoped_testing_local_state_;
+  }
+
+  void SetUp() override {
+    testing::Test::SetUp();
+    OSCryptMocker::SetUp();
+  }
+
+  void TearDown() override {
+    OSCryptMocker::TearDown();
+    testing::Test::TearDown();
+  }
+
+ private:
+  ScopedTestingLocalState scoped_testing_local_state_;
+};
+
+TEST_F(DeviceOAuth2TokenStoreDesktopTest, InitWithoutSavedToken) {
+  DeviceOAuth2TokenStoreDesktop store(scoped_testing_local_state()->Get());
+
+  EXPECT_TRUE(store.GetAccountId().empty());
+  EXPECT_TRUE(store.GetRefreshToken().empty());
+
+  store.Init(base::BindOnce([](bool, bool) {}));
+
+  TestObserver observer;
+  store.SetObserver(&observer);
+
+  // Observer shouldn't have been called because there's no account ID available
+  // yet.
+  EXPECT_EQ(0, observer.called_count());
+  EXPECT_TRUE(store.GetAccountId().empty());
+  EXPECT_TRUE(store.GetRefreshToken().empty());
+}
+
+TEST_F(DeviceOAuth2TokenStoreDesktopTest, InitWithSavedToken) {
+  scoped_testing_local_state()->Get()->SetString(kCBCMServiceAccountEmail,
+                                                 "[email protected]");
+
+  std::string token = "test_token";
+  std::string encrypted_token;
+  OSCrypt::EncryptString(token, &encrypted_token);
+
+  std::string encoded;
+  base::Base64Encode(encrypted_token, &encoded);
+
+  scoped_testing_local_state()->Get()->SetString(
+      kCBCMServiceAccountRefreshToken, encoded);
+
+  DeviceOAuth2TokenStoreDesktop store(scoped_testing_local_state()->Get());
+
+  EXPECT_TRUE(store.GetRefreshToken().empty());
+
+  TestObserver observer;
+  store.SetObserver(&observer);
+
+  store.Init(base::BindOnce([](bool, bool) {}));
+
+  EXPECT_EQ(1, observer.called_count());
+  EXPECT_EQ(store.GetAccountId(), CoreAccountId::FromEmail("[email protected]"));
+  EXPECT_EQ(store.GetRefreshToken(), token);
+}
+
+TEST_F(DeviceOAuth2TokenStoreDesktopTest, ObserverNotifiedWhenAccountChanges) {
+  scoped_testing_local_state()->Get()->SetString(kCBCMServiceAccountEmail,
+                                                 "[email protected]");
+
+  std::string token = "test_token";
+  std::string encrypted_token;
+  OSCrypt::EncryptString(token, &encrypted_token);
+
+  std::string encoded;
+  base::Base64Encode(encrypted_token, &encoded);
+
+  scoped_testing_local_state()->Get()->SetString(
+      kCBCMServiceAccountRefreshToken, encoded);
+
+  DeviceOAuth2TokenStoreDesktop store(scoped_testing_local_state()->Get());
+
+  TestObserver test_observer;
+  store.SetObserver(&test_observer);
+
+  EXPECT_TRUE(store.GetRefreshToken().empty());
+
+  store.Init(base::BindOnce([](bool, bool) {}));
+
+  EXPECT_EQ(1, test_observer.called_count());
+
+  EXPECT_EQ(store.GetAccountId(), CoreAccountId::FromEmail("[email protected]"));
+  EXPECT_EQ(store.GetRefreshToken(), token);
+
+  store.SetAccountEmail("[email protected]");
+
+  EXPECT_EQ(2, test_observer.called_count());
+}
+
+TEST_F(DeviceOAuth2TokenStoreDesktopTest, SaveToken) {
+  std::string token = "test_token";
+
+  DeviceOAuth2TokenStoreDesktop store(scoped_testing_local_state()->Get());
+  store.Init(base::BindOnce([](bool, bool) {}));
+
+  EXPECT_TRUE(store.GetRefreshToken().empty());
+
+  bool callback_success = false;
+  store.SetAndSaveRefreshToken(
+      token, base::BindLambdaForTesting([&callback_success](bool success) {
+        callback_success = success;
+      }));
+
+  EXPECT_TRUE(callback_success);
+
+  std::string persisted_token = scoped_testing_local_state()->Get()->GetString(
+      kCBCMServiceAccountRefreshToken);
+
+  std::string decoded;
+  base::Base64Decode(persisted_token, &decoded);
+  std::string decrypted;
+  OSCrypt::DecryptString(decoded, &decrypted);
+
+  EXPECT_EQ(token, store.GetRefreshToken());
+  EXPECT_EQ(token, decrypted);
+}
diff --git a/chrome/browser/extensions/api/identity/identity_get_auth_token_function.cc b/chrome/browser/extensions/api/identity/identity_get_auth_token_function.cc
index d93c41b..4aa815f 100644
--- a/chrome/browser/extensions/api/identity/identity_get_auth_token_function.cc
+++ b/chrome/browser/extensions/api/identity/identity_get_auth_token_function.cc
@@ -100,9 +100,8 @@
   std::unique_ptr<api::identity::GetAuthToken::Params> params(
       api::identity::GetAuthToken::Params::Create(*args_));
   EXTENSION_FUNCTION_VALIDATE(params.get());
-  interactive_ = params->details.get() &&
-      params->details->interactive.get() &&
-      *params->details->interactive;
+  interactive_ = params->details.get() && params->details->interactive.get() &&
+                 *params->details->interactive;
 
   should_prompt_for_scopes_ = interactive_;
   should_prompt_for_signin_ =
@@ -414,8 +413,7 @@
   const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(extension());
   IdentityAPI* id_api = IdentityAPI::GetFactoryInstance()->Get(GetProfile());
   IdentityTokenCacheValue cache_entry = id_api->GetCachedToken(token_key_);
-  IdentityTokenCacheValue::CacheValueStatus cache_status =
-      cache_entry.status();
+  IdentityTokenCacheValue::CacheValueStatus cache_status = cache_entry.status();
 
   if (type == IdentityMintRequestQueue::MINT_TYPE_NONINTERACTIVE) {
     switch (cache_status) {
@@ -545,8 +543,8 @@
       break;
   }
 
-  CompleteFunctionWithError(
-      std::string(identity_constants::kAuthFailure) + error.ToString());
+  CompleteFunctionWithError(std::string(identity_constants::kAuthFailure) +
+                            error.ToString());
 }
 
 void IdentityGetAuthTokenFunction::OnIssueAdviceSuccess(
@@ -659,7 +657,7 @@
         }
       }
       error = std::string(identity_constants::kAuthFailure) +
-          service_error.ToString();
+              service_error.ToString();
       break;
 
     case GaiaWebAuthFlow::OAUTH_ERROR:
@@ -824,9 +822,12 @@
 }
 
 #if defined(OS_CHROMEOS)
+// Even though the DeviceOAuth2TokenService may be available on non-ChromeOS
+// platforms, its robot account is not made available because it should only be
+// used for very specific policy-related things. In fact, the device account on
+// desktop isn't scoped for anything other than policy invalidations.
 void IdentityGetAuthTokenFunction::StartDeviceAccessTokenRequest() {
-  chromeos::DeviceOAuth2TokenService* service =
-      chromeos::DeviceOAuth2TokenServiceFactory::Get();
+  DeviceOAuth2TokenService* service = DeviceOAuth2TokenServiceFactory::Get();
   // Since robot account refresh tokens are scoped down to [any-api] only,
   // request access token for [any-api] instead of login.
   OAuth2AccessTokenManager::ScopeSet scopes;
@@ -856,9 +857,9 @@
   if (chrome::IsRunningInForcedAppMode()) {
     std::string app_client_id;
     std::string app_client_secret;
-    if (chromeos::UserSessionManager::GetInstance()->
-            GetAppModeChromeClientOAuthInfo(&app_client_id,
-                                            &app_client_secret)) {
+    if (chromeos::UserSessionManager::GetInstance()
+            ->GetAppModeChromeClientOAuthInfo(&app_client_id,
+                                              &app_client_secret)) {
       token_key_account_access_token_fetcher_ =
           identity_manager->CreateAccessTokenFetcherForClient(
               token_key_.account_id, app_client_id, app_client_secret,
diff --git a/chrome/browser/invalidation/profile_invalidation_provider_factory.cc b/chrome/browser/invalidation/profile_invalidation_provider_factory.cc
index be1fd42..d4d8440 100644
--- a/chrome/browser/invalidation/profile_invalidation_provider_factory.cc
+++ b/chrome/browser/invalidation/profile_invalidation_provider_factory.cc
@@ -125,8 +125,8 @@
   if (user_manager::UserManager::IsInitialized() &&
       user_manager::UserManager::Get()->IsLoggedInAsKioskApp() &&
       connector->IsEnterpriseManaged()) {
-    identity_provider.reset(new chromeos::DeviceIdentityProvider(
-        chromeos::DeviceOAuth2TokenServiceFactory::Get()));
+    identity_provider = std::make_unique<DeviceIdentityProvider>(
+        DeviceOAuth2TokenServiceFactory::Get());
   }
 #endif  // defined(OS_CHROMEOS)
 
diff --git a/chrome/browser/policy/chrome_browser_cloud_management_controller.cc b/chrome/browser/policy/chrome_browser_cloud_management_controller.cc
index 8bebe43..b5e25fd 100644
--- a/chrome/browser/policy/chrome_browser_cloud_management_controller.cc
+++ b/chrome/browser/policy/chrome_browser_cloud_management_controller.cc
@@ -18,6 +18,8 @@
 #include "build/branding_buildflags.h"
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/device_identity/device_oauth2_token_service.h"
+#include "chrome/browser/device_identity/device_oauth2_token_service_factory.h"
 #include "chrome/browser/enterprise_reporting/report_generator.h"
 #include "chrome/browser/enterprise_reporting/report_scheduler.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
@@ -342,6 +344,12 @@
     UnenrollBrowser();
 }
 
+void ChromeBrowserCloudManagementController::OnServiceAccountChanged(
+    CloudPolicyClient* client) {
+  DeviceOAuth2TokenServiceFactory::Get()->SetServiceAccountEmail(
+      client->service_account_email());
+}
+
 void ChromeBrowserCloudManagementController::ShutDown() {
   if (report_scheduler_)
     report_scheduler_.reset();
diff --git a/chrome/browser/policy/chrome_browser_cloud_management_controller.h b/chrome/browser/policy/chrome_browser_cloud_management_controller.h
index eb73a9b..556264c 100644
--- a/chrome/browser/policy/chrome_browser_cloud_management_controller.h
+++ b/chrome/browser/policy/chrome_browser_cloud_management_controller.h
@@ -102,6 +102,7 @@
   void OnPolicyFetched(CloudPolicyClient* client) override;
   void OnRegistrationStateChanged(CloudPolicyClient* client) override;
   void OnClientError(CloudPolicyClient* client) override;
+  void OnServiceAccountChanged(CloudPolicyClient* client) override;
 
   // Early cleanup during browser shutdown process
   void ShutDown();
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 1d661bc..39e4dbc 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -315,7 +315,7 @@
 #include "chrome/browser/chromeos/settings/device_settings_cache.h"
 #include "chrome/browser/chromeos/system/automatic_reboot_manager.h"
 #include "chrome/browser/chromeos/system/input_device_settings.h"
-#include "chrome/browser/device_identity/device_oauth2_token_service.h"
+#include "chrome/browser/device_identity/chromeos/device_oauth2_token_store_chromeos.h"
 #include "chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api.h"
 #include "chrome/browser/extensions/extension_assets_manager_chromeos.h"
 #include "chrome/browser/media/protected_media_identifier_permission_context.h"
@@ -377,8 +377,10 @@
 #endif
 
 #if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
+#include "chrome/browser/device_identity//device_oauth2_token_store_desktop.h"
 #include "chrome/browser/downgrade/downgrade_prefs.h"
 #include "chrome/browser/ui/startup/default_browser_prompt.h"
+
 #endif
 
 #if defined(TOOLKIT_VIEWS)
@@ -719,7 +721,7 @@
   chromeos::DemoModeResourcesRemover::RegisterLocalStatePrefs(registry);
   chromeos::DemoSession::RegisterLocalStatePrefs(registry);
   chromeos::DemoSetupController::RegisterLocalStatePrefs(registry);
-  chromeos::DeviceOAuth2TokenService::RegisterPrefs(registry);
+  chromeos::DeviceOAuth2TokenStoreChromeOS::RegisterPrefs(registry);
   chromeos::device_settings_cache::RegisterPrefs(registry);
   chromeos::EasyUnlockService::RegisterPrefs(registry);
   chromeos::echo_offer::RegisterPrefs(registry);
@@ -806,6 +808,7 @@
 #if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
   RegisterDefaultBrowserPromptPrefs(registry);
   downgrade::RegisterPrefs(registry);
+  DeviceOAuth2TokenStoreDesktop::RegisterPrefs(registry);
 #endif
 
   // Obsolete. See MigrateObsoleteBrowserPrefs().
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
index c0823ff..113d342 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
@@ -490,8 +490,8 @@
     const signin::ScopeSet scopes{cloud_devices::kCloudPrintAuthScope};
     DCHECK(!device_request_callback_);
 
-    chromeos::DeviceOAuth2TokenService* token_service =
-        chromeos::DeviceOAuth2TokenServiceFactory::Get();
+    DeviceOAuth2TokenService* token_service =
+        DeviceOAuth2TokenServiceFactory::Get();
     device_request_ = token_service->StartAccessTokenRequest(scopes, this);
     device_request_callback_ = std::move(callback);
   }