1c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
2c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
3c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// found in the LICENSE file.
4c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
5c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chrome/browser/chromeos/settings/device_oauth2_token_service.h"
6c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
7eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include <string>
8eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include <vector>
9eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
10a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "base/bind.h"
11a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "base/memory/weak_ptr.h"
12a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "base/message_loop/message_loop.h"
13c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/prefs/pref_registry_simple.h"
14c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/prefs/pref_service.h"
15eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/values.h"
16eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "chrome/browser/browser_process.h"
17a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "chrome/browser/chromeos/settings/cros_settings.h"
184e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "chrome/browser/chromeos/settings/token_encryptor.h"
19c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chrome/common/pref_names.h"
20a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "chromeos/cryptohome/system_salt_getter.h"
21a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "google_apis/gaia/gaia_constants.h"
22eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "google_apis/gaia/gaia_urls.h"
23eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "google_apis/gaia/google_service_auth_error.h"
24a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "google_apis/gaia/oauth2_access_token_fetcher_impl.h"
25a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "policy/proto/device_management_backend.pb.h"
26eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
27c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)namespace chromeos {
28c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
29a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)struct DeviceOAuth2TokenService::PendingRequest {
30a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  PendingRequest(const base::WeakPtr<RequestImpl>& request,
31a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                 const std::string& client_id,
32a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                 const std::string& client_secret,
33a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                 const ScopeSet& scopes)
34a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      : request(request),
35a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        client_id(client_id),
36a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        client_secret(client_secret),
37a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        scopes(scopes) {}
38a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
39a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  const base::WeakPtr<RequestImpl> request;
40a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  const std::string client_id;
41a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  const std::string client_secret;
42a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  const ScopeSet scopes;
43eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch};
44eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
45a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)DeviceOAuth2TokenService::DeviceOAuth2TokenService(
46a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    net::URLRequestContextGetter* getter,
47a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    PrefService* local_state)
48a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    : url_request_context_getter_(getter),
49a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      local_state_(local_state),
50a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      state_(STATE_LOADING),
51a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      max_refresh_token_validation_retries_(3),
52a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      weak_ptr_factory_(this) {
53a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // Pull in the system salt.
54a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  SystemSaltGetter::Get()->GetSystemSalt(
55a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      base::Bind(&DeviceOAuth2TokenService::DidGetSystemSalt,
56a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                 weak_ptr_factory_.GetWeakPtr()));
57eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
58eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
59a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)DeviceOAuth2TokenService::~DeviceOAuth2TokenService() {
60a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  FlushPendingRequests(false, GoogleServiceAuthError::REQUEST_CANCELED);
61a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  FlushTokenSaveCallbacks(false);
62eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
63eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
64a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// static
65a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void DeviceOAuth2TokenService::RegisterPrefs(PrefRegistrySimple* registry) {
66a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  registry->RegisterStringPref(prefs::kDeviceRobotAnyApiRefreshToken,
67a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                               std::string());
68a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
69eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
70a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void DeviceOAuth2TokenService::SetAndSaveRefreshToken(
71a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const std::string& refresh_token,
72a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const StatusCallback& result_callback) {
73a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  FlushPendingRequests(false, GoogleServiceAuthError::REQUEST_CANCELED);
74a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
75a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  bool waiting_for_salt = state_ == STATE_LOADING;
76a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  refresh_token_ = refresh_token;
77a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  state_ = STATE_VALIDATION_PENDING;
78a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  FireRefreshTokenAvailable(GetRobotAccountId());
79a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
80a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  token_save_callbacks_.push_back(result_callback);
81a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (!waiting_for_salt) {
82a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    if (system_salt_.empty())
83a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      FlushTokenSaveCallbacks(false);
84a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    else
85a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      EncryptAndSaveToken();
86a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
87a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
88eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
89a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)bool DeviceOAuth2TokenService::RefreshTokenIsAvailable(
90a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const std::string& account_id) const {
91a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  switch (state_) {
92a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    case STATE_NO_TOKEN:
93a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    case STATE_TOKEN_INVALID:
94a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      return false;
95a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    case STATE_LOADING:
96a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    case STATE_VALIDATION_PENDING:
97a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    case STATE_VALIDATION_STARTED:
98a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    case STATE_TOKEN_VALID:
99a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      return account_id == GetRobotAccountId();
100a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
101a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
102a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  NOTREACHED() << "Unhandled state " << state_;
103a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return false;
104a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
105a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
106a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)std::string DeviceOAuth2TokenService::GetRobotAccountId() const {
107a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  std::string result;
108a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  CrosSettings::Get()->GetString(kServiceAccountIdentity, &result);
109a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return result;
110eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
111eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
112a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void DeviceOAuth2TokenService::OnRefreshTokenResponse(
113eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    const std::string& access_token,
114eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    int expires_in_seconds) {
115eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  gaia_oauth_client_->GetTokenInfo(
116eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      access_token,
117a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      max_refresh_token_validation_retries_,
118eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      this);
119eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
120eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
121a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void DeviceOAuth2TokenService::OnGetTokenInfoResponse(
1225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    scoped_ptr<base::DictionaryValue> token_info) {
123eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  std::string gaia_robot_id;
124eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  token_info->GetString("email", &gaia_robot_id);
125a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  gaia_oauth_client_.reset();
126eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
127a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  CheckRobotAccountId(gaia_robot_id);
128eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
129eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
130a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void DeviceOAuth2TokenService::OnOAuthError() {
131a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  gaia_oauth_client_.reset();
132a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  state_ = STATE_TOKEN_INVALID;
133a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  FlushPendingRequests(false, GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
134eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
135eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
136a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void DeviceOAuth2TokenService::OnNetworkError(int response_code) {
137a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  gaia_oauth_client_.reset();
138eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
139a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // Go back to pending validation state. That'll allow a retry on subsequent
140a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // token minting requests.
141a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  state_ = STATE_VALIDATION_PENDING;
142a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  FlushPendingRequests(false, GoogleServiceAuthError::CONNECTION_FAILED);
143eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
144eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
145a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)std::string DeviceOAuth2TokenService::GetRefreshToken(
146a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const std::string& account_id) const {
147a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  switch (state_) {
148a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    case STATE_LOADING:
149a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    case STATE_NO_TOKEN:
150a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    case STATE_TOKEN_INVALID:
151a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      // This shouldn't happen: GetRefreshToken() is only called for actual
152a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      // token minting operations. In above states, requests are either queued
153a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      // or short-circuited to signal error immediately, so no actual token
154a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      // minting via OAuth2TokenService::FetchOAuth2Token should be triggered.
155a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      NOTREACHED();
156a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      return std::string();
157a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    case STATE_VALIDATION_PENDING:
158a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    case STATE_VALIDATION_STARTED:
159a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    case STATE_TOKEN_VALID:
160a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      return refresh_token_;
161a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
162a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
163a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  NOTREACHED() << "Unhandled state " << state_;
164a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return std::string();
165eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
166eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
167a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)net::URLRequestContextGetter* DeviceOAuth2TokenService::GetRequestContext() {
168a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return url_request_context_getter_.get();
169eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
170eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
171a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void DeviceOAuth2TokenService::FetchOAuth2Token(
172a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    RequestImpl* request,
173a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const std::string& account_id,
174a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    net::URLRequestContextGetter* getter,
175a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const std::string& client_id,
176a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const std::string& client_secret,
177a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const ScopeSet& scopes) {
178a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  switch (state_) {
179a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    case STATE_VALIDATION_PENDING:
180a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      // If this is the first request for a token, start validation.
181a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      StartValidation();
182a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      // fall through.
183a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    case STATE_LOADING:
184a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    case STATE_VALIDATION_STARTED:
185a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      // Add a pending request that will be satisfied once validation completes.
186a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      pending_requests_.push_back(new PendingRequest(
187a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          request->AsWeakPtr(), client_id, client_secret, scopes));
188a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      return;
189a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    case STATE_NO_TOKEN:
190a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      FailRequest(request, GoogleServiceAuthError::USER_NOT_SIGNED_UP);
191a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      return;
192a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    case STATE_TOKEN_INVALID:
193a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      FailRequest(request, GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
194a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      return;
195a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    case STATE_TOKEN_VALID:
196a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      // Pass through to OAuth2TokenService to satisfy the request.
197a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      OAuth2TokenService::FetchOAuth2Token(
198a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          request, account_id, getter, client_id, client_secret, scopes);
199a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      return;
200eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  }
201a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
202a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  NOTREACHED() << "Unexpected state " << state_;
203eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
204eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
205a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)OAuth2AccessTokenFetcher* DeviceOAuth2TokenService::CreateAccessTokenFetcher(
206a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const std::string& account_id,
207c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    net::URLRequestContextGetter* getter,
208a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    OAuth2AccessTokenConsumer* consumer) {
209a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  std::string refresh_token = GetRefreshToken(account_id);
210a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  DCHECK(!refresh_token.empty());
211a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return new OAuth2AccessTokenFetcherImpl(consumer, getter, refresh_token);
212c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
213c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
214eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
215a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void DeviceOAuth2TokenService::DidGetSystemSalt(
216a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const std::string& system_salt) {
217a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  system_salt_ = system_salt;
218a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
219a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // Bail out if system salt is not available.
220a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (system_salt_.empty()) {
221a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    LOG(ERROR) << "Failed to get system salt.";
222a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    FlushTokenSaveCallbacks(false);
223a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    state_ = STATE_NO_TOKEN;
224a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    FireRefreshTokensLoaded();
225a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return;
226a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
227a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
228a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // If the token has been set meanwhile, write it to |local_state_|.
229a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (!refresh_token_.empty()) {
230a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    EncryptAndSaveToken();
231a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    FireRefreshTokensLoaded();
232a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return;
233a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
234a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
235a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // Otherwise, load the refresh token from |local_state_|.
236a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  std::string encrypted_refresh_token =
237a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      local_state_->GetString(prefs::kDeviceRobotAnyApiRefreshToken);
238116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (!encrypted_refresh_token.empty()) {
239116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    CryptohomeTokenEncryptor encryptor(system_salt_);
240116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    refresh_token_ = encryptor.DecryptWithSystemSalt(encrypted_refresh_token);
241116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    if (refresh_token_.empty()) {
242116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      LOG(ERROR) << "Failed to decrypt refresh token.";
243116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      state_ = STATE_NO_TOKEN;
244116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      FireRefreshTokensLoaded();
245116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      return;
246116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    }
247a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
248a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
249a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  state_ = STATE_VALIDATION_PENDING;
250a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
251a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // If there are pending requests, start a validation.
252a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (!pending_requests_.empty())
253a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    StartValidation();
254a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
255a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // Announce the token.
256a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  FireRefreshTokenAvailable(GetRobotAccountId());
257a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  FireRefreshTokensLoaded();
258c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
259c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
260a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void DeviceOAuth2TokenService::CheckRobotAccountId(
261a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const std::string& gaia_robot_id) {
262a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // Make sure the value returned by GetRobotAccountId has been validated
263a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // against current device settings.
264a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  switch (CrosSettings::Get()->PrepareTrustedValues(
265a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      base::Bind(&DeviceOAuth2TokenService::CheckRobotAccountId,
266a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                 weak_ptr_factory_.GetWeakPtr(),
267a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                 gaia_robot_id))) {
268a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    case CrosSettingsProvider::TRUSTED:
269a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      // All good, compare account ids below.
270a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      break;
271a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    case CrosSettingsProvider::TEMPORARILY_UNTRUSTED:
272a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      // The callback passed to PrepareTrustedValues above will trigger a
273a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      // re-check eventually.
274a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      return;
275a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    case CrosSettingsProvider::PERMANENTLY_UNTRUSTED:
276a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      // There's no trusted account id, which is equivalent to no token present.
277a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      LOG(WARNING) << "Device settings permanently untrusted.";
278a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      state_ = STATE_NO_TOKEN;
279a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      FlushPendingRequests(false, GoogleServiceAuthError::USER_NOT_SIGNED_UP);
280a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      return;
281a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
282a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
283a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  std::string policy_robot_id = GetRobotAccountId();
284a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (policy_robot_id == gaia_robot_id) {
285a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    state_ = STATE_TOKEN_VALID;
286a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    FlushPendingRequests(true, GoogleServiceAuthError::NONE);
287a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  } else {
288a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    if (gaia_robot_id.empty()) {
289a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      LOG(WARNING) << "Device service account owner in policy is empty.";
290a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    } else {
291a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      LOG(WARNING) << "Device service account owner in policy does not match "
292a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                   << "refresh token owner \"" << gaia_robot_id << "\".";
293a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
294a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    state_ = STATE_TOKEN_INVALID;
295a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    FlushPendingRequests(false,
296a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                         GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
297a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
298c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
299c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
300a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void DeviceOAuth2TokenService::EncryptAndSaveToken() {
301a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  DCHECK_NE(state_, STATE_LOADING);
3024e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
303a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  CryptohomeTokenEncryptor encryptor(system_salt_);
304c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  std::string encrypted_refresh_token =
305a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      encryptor.EncryptWithSystemSalt(refresh_token_);
306a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  bool result = true;
3071e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  if (encrypted_refresh_token.empty()) {
3081e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    LOG(ERROR) << "Failed to encrypt refresh token; save aborted.";
309a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    result = false;
310a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  } else {
311a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    local_state_->SetString(prefs::kDeviceRobotAnyApiRefreshToken,
312a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                            encrypted_refresh_token);
3131e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  }
314c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
315a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  FlushTokenSaveCallbacks(result);
316c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
317c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
318a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void DeviceOAuth2TokenService::StartValidation() {
319a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  DCHECK_EQ(state_, STATE_VALIDATION_PENDING);
320a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  DCHECK(!gaia_oauth_client_);
321a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
322a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  state_ = STATE_VALIDATION_STARTED;
323a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
324a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  gaia_oauth_client_.reset(new gaia::GaiaOAuthClient(
325a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      g_browser_process->system_request_context()));
326a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
327a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  GaiaUrls* gaia_urls = GaiaUrls::GetInstance();
328a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  gaia::OAuthClientInfo client_info;
329a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  client_info.client_id = gaia_urls->oauth2_chrome_client_id();
330a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  client_info.client_secret = gaia_urls->oauth2_chrome_client_secret();
331a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
332a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  gaia_oauth_client_->RefreshToken(
333a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      client_info,
334a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      refresh_token_,
335a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      std::vector<std::string>(1, GaiaConstants::kOAuthWrapBridgeUserInfoScope),
336a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      max_refresh_token_validation_retries_,
337a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      this);
338c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
339c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
340a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void DeviceOAuth2TokenService::FlushPendingRequests(
341a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    bool token_is_valid,
342a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    GoogleServiceAuthError::State error) {
343a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  std::vector<PendingRequest*> requests;
344a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  requests.swap(pending_requests_);
345a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  for (std::vector<PendingRequest*>::iterator request(requests.begin());
346a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)       request != requests.end();
347a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)       ++request) {
348a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    scoped_ptr<PendingRequest> scoped_request(*request);
349a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    if (!scoped_request->request)
350a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      continue;
351a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
352a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    if (token_is_valid) {
353a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      OAuth2TokenService::FetchOAuth2Token(
354a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          scoped_request->request.get(),
355a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          scoped_request->request->GetAccountId(),
356a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          GetRequestContext(),
357a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          scoped_request->client_id,
358a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          scoped_request->client_secret,
359a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          scoped_request->scopes);
360a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    } else {
361a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      FailRequest(scoped_request->request.get(), error);
362a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
363a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
364eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
365eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
366a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void DeviceOAuth2TokenService::FlushTokenSaveCallbacks(bool result) {
367a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  std::vector<StatusCallback> callbacks;
368a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  callbacks.swap(token_save_callbacks_);
369a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  for (std::vector<StatusCallback>::iterator callback(callbacks.begin());
370a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)       callback != callbacks.end();
371a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)       ++callback) {
372a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    if (!callback->is_null())
373a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      callback->Run(result);
374a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
3753551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)}
3763551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
377a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void DeviceOAuth2TokenService::FailRequest(
378a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    RequestImpl* request,
379a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    GoogleServiceAuthError::State error) {
380a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  GoogleServiceAuthError auth_error(error);
381a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
382a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      &RequestImpl::InformConsumer,
383a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      request->AsWeakPtr(),
384a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      auth_error,
385a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      std::string(),
386a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      base::Time()));
38768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)}
38868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
389c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}  // namespace chromeos
390