1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/chromeos/settings/device_oauth2_token_service_delegate.h"
10 #include "base/bind.h"
11 #include "base/bind_helpers.h"
12 #include "base/memory/weak_ptr.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/prefs/pref_registry_simple.h"
15 #include "base/prefs/pref_service.h"
16 #include "base/values.h"
17 #include "chrome/browser/browser_process.h"
18 #include "chrome/browser/chromeos/settings/token_encryptor.h"
19 #include "chrome/common/pref_names.h"
20 #include "chromeos/cryptohome/system_salt_getter.h"
21 #include "chromeos/settings/cros_settings_names.h"
22 #include "google_apis/gaia/gaia_constants.h"
23 #include "google_apis/gaia/gaia_urls.h"
24 #include "google_apis/gaia/google_service_auth_error.h"
25 #include "google_apis/gaia/oauth2_access_token_fetcher_impl.h"
26 #include "policy/proto/device_management_backend.pb.h"
30 void DeviceOAuth2TokenServiceDelegate::OnServiceAccountIdentityChanged() {
31 if (!GetRobotAccountId().empty() && !refresh_token_
.empty())
32 FireRefreshTokenAvailable(GetRobotAccountId());
35 DeviceOAuth2TokenServiceDelegate::DeviceOAuth2TokenServiceDelegate(
36 net::URLRequestContextGetter
* getter
,
37 PrefService
* local_state
)
38 : url_request_context_getter_(getter
),
39 local_state_(local_state
),
40 state_(STATE_LOADING
),
41 max_refresh_token_validation_retries_(3),
42 validation_requested_(false),
43 validation_status_delegate_(nullptr),
44 service_account_identity_subscription_(
46 ->AddSettingsObserver(
47 kServiceAccountIdentity
,
48 base::Bind(&DeviceOAuth2TokenServiceDelegate::
49 OnServiceAccountIdentityChanged
,
50 base::Unretained(this)))
52 weak_ptr_factory_(this) {
53 // Pull in the system salt.
54 SystemSaltGetter::Get()->GetSystemSalt(
55 base::Bind(&DeviceOAuth2TokenServiceDelegate::DidGetSystemSalt
,
56 weak_ptr_factory_
.GetWeakPtr()));
59 DeviceOAuth2TokenServiceDelegate::~DeviceOAuth2TokenServiceDelegate() {
60 FlushTokenSaveCallbacks(false);
63 void DeviceOAuth2TokenServiceDelegate::SetAndSaveRefreshToken(
64 const std::string
& refresh_token
,
65 const StatusCallback
& result_callback
) {
66 ReportServiceError(GoogleServiceAuthError::REQUEST_CANCELED
);
68 bool waiting_for_salt
= state_
== STATE_LOADING
;
69 refresh_token_
= refresh_token
;
70 state_
= STATE_VALIDATION_PENDING
;
72 // If the robot account ID is not available yet, do not announce the token. It
73 // will be done from OnServiceAccountIdentityChanged() once the robot account
74 // ID becomes available as well.
75 if (!GetRobotAccountId().empty())
76 FireRefreshTokenAvailable(GetRobotAccountId());
78 token_save_callbacks_
.push_back(result_callback
);
79 if (!waiting_for_salt
) {
80 if (system_salt_
.empty())
81 FlushTokenSaveCallbacks(false);
83 EncryptAndSaveToken();
87 bool DeviceOAuth2TokenServiceDelegate::RefreshTokenIsAvailable(
88 const std::string
& account_id
) const {
91 case STATE_TOKEN_INVALID
:
94 case STATE_VALIDATION_PENDING
:
95 case STATE_VALIDATION_STARTED
:
96 case STATE_TOKEN_VALID
:
97 return account_id
== GetRobotAccountId();
100 NOTREACHED() << "Unhandled state " << state_
;
104 std::string
DeviceOAuth2TokenServiceDelegate::GetRobotAccountId() const {
106 CrosSettings::Get()->GetString(kServiceAccountIdentity
, &result
);
110 void DeviceOAuth2TokenServiceDelegate::OnRefreshTokenResponse(
111 const std::string
& access_token
,
112 int expires_in_seconds
) {
113 gaia_oauth_client_
->GetTokenInfo(access_token
,
114 max_refresh_token_validation_retries_
, this);
117 void DeviceOAuth2TokenServiceDelegate::OnGetTokenInfoResponse(
118 scoped_ptr
<base::DictionaryValue
> token_info
) {
119 std::string gaia_robot_id
;
120 token_info
->GetString("email", &gaia_robot_id
);
121 gaia_oauth_client_
.reset();
123 CheckRobotAccountId(gaia_robot_id
);
126 void DeviceOAuth2TokenServiceDelegate::OnOAuthError() {
127 gaia_oauth_client_
.reset();
128 state_
= STATE_TOKEN_INVALID
;
129 ReportServiceError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS
);
132 void DeviceOAuth2TokenServiceDelegate::OnNetworkError(int response_code
) {
133 gaia_oauth_client_
.reset();
135 // Go back to pending validation state. That'll allow a retry on subsequent
136 // token minting requests.
137 state_
= STATE_VALIDATION_PENDING
;
138 ReportServiceError(GoogleServiceAuthError::CONNECTION_FAILED
);
141 std::string
DeviceOAuth2TokenServiceDelegate::GetRefreshToken(
142 const std::string
& account_id
) const {
146 case STATE_TOKEN_INVALID
:
147 // This shouldn't happen: GetRefreshToken() is only called for actual
148 // token minting operations. In above states, requests are either queued
149 // or short-circuited to signal error immediately, so no actual token
150 // minting via OAuth2TokenService::FetchOAuth2Token should be triggered.
152 return std::string();
153 case STATE_VALIDATION_PENDING
:
154 case STATE_VALIDATION_STARTED
:
155 case STATE_TOKEN_VALID
:
156 return refresh_token_
;
159 NOTREACHED() << "Unhandled state " << state_
;
160 return std::string();
163 net::URLRequestContextGetter
*
164 DeviceOAuth2TokenServiceDelegate::GetRequestContext() const {
165 return url_request_context_getter_
.get();
168 OAuth2AccessTokenFetcher
*
169 DeviceOAuth2TokenServiceDelegate::CreateAccessTokenFetcher(
170 const std::string
& account_id
,
171 net::URLRequestContextGetter
* getter
,
172 OAuth2AccessTokenConsumer
* consumer
) {
173 std::string refresh_token
= GetRefreshToken(account_id
);
174 DCHECK(!refresh_token
.empty());
175 return new OAuth2AccessTokenFetcherImpl(consumer
, getter
, refresh_token
);
178 void DeviceOAuth2TokenServiceDelegate::DidGetSystemSalt(
179 const std::string
& system_salt
) {
180 system_salt_
= system_salt
;
182 // Bail out if system salt is not available.
183 if (system_salt_
.empty()) {
184 LOG(ERROR
) << "Failed to get system salt.";
185 FlushTokenSaveCallbacks(false);
186 state_
= STATE_NO_TOKEN
;
187 FireRefreshTokensLoaded();
191 // If the token has been set meanwhile, write it to |local_state_|.
192 if (!refresh_token_
.empty()) {
193 EncryptAndSaveToken();
194 FireRefreshTokensLoaded();
198 // Otherwise, load the refresh token from |local_state_|.
199 std::string encrypted_refresh_token
=
200 local_state_
->GetString(prefs::kDeviceRobotAnyApiRefreshToken
);
201 if (!encrypted_refresh_token
.empty()) {
202 CryptohomeTokenEncryptor
encryptor(system_salt_
);
203 refresh_token_
= encryptor
.DecryptWithSystemSalt(encrypted_refresh_token
);
204 if (refresh_token_
.empty()) {
205 LOG(ERROR
) << "Failed to decrypt refresh token.";
206 state_
= STATE_NO_TOKEN
;
207 FireRefreshTokensLoaded();
212 state_
= STATE_VALIDATION_PENDING
;
214 // If there are pending requests, start a validation.
215 if (validation_requested_
)
218 // Announce the token.
219 FireRefreshTokenAvailable(GetRobotAccountId());
220 FireRefreshTokensLoaded();
223 void DeviceOAuth2TokenServiceDelegate::CheckRobotAccountId(
224 const std::string
& gaia_robot_id
) {
225 // Make sure the value returned by GetRobotAccountId has been validated
226 // against current device settings.
227 switch (CrosSettings::Get()->PrepareTrustedValues(
228 base::Bind(&DeviceOAuth2TokenServiceDelegate::CheckRobotAccountId
,
229 weak_ptr_factory_
.GetWeakPtr(), gaia_robot_id
))) {
230 case CrosSettingsProvider::TRUSTED
:
231 // All good, compare account ids below.
233 case CrosSettingsProvider::TEMPORARILY_UNTRUSTED
:
234 // The callback passed to PrepareTrustedValues above will trigger a
235 // re-check eventually.
237 case CrosSettingsProvider::PERMANENTLY_UNTRUSTED
:
238 // There's no trusted account id, which is equivalent to no token present.
239 LOG(WARNING
) << "Device settings permanently untrusted.";
240 state_
= STATE_NO_TOKEN
;
241 ReportServiceError(GoogleServiceAuthError::USER_NOT_SIGNED_UP
);
245 std::string policy_robot_id
= GetRobotAccountId();
246 if (policy_robot_id
== gaia_robot_id
) {
247 state_
= STATE_TOKEN_VALID
;
248 ReportServiceError(GoogleServiceAuthError::NONE
);
250 if (gaia_robot_id
.empty()) {
251 LOG(WARNING
) << "Device service account owner in policy is empty.";
253 LOG(WARNING
) << "Device service account owner in policy does not match "
254 << "refresh token owner \"" << gaia_robot_id
<< "\".";
256 state_
= STATE_TOKEN_INVALID
;
257 ReportServiceError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS
);
261 void DeviceOAuth2TokenServiceDelegate::EncryptAndSaveToken() {
262 DCHECK_NE(state_
, STATE_LOADING
);
264 CryptohomeTokenEncryptor
encryptor(system_salt_
);
265 std::string encrypted_refresh_token
=
266 encryptor
.EncryptWithSystemSalt(refresh_token_
);
268 if (encrypted_refresh_token
.empty()) {
269 LOG(ERROR
) << "Failed to encrypt refresh token; save aborted.";
272 local_state_
->SetString(prefs::kDeviceRobotAnyApiRefreshToken
,
273 encrypted_refresh_token
);
276 FlushTokenSaveCallbacks(result
);
279 void DeviceOAuth2TokenServiceDelegate::StartValidation() {
280 DCHECK_EQ(state_
, STATE_VALIDATION_PENDING
);
281 DCHECK(!gaia_oauth_client_
);
283 state_
= STATE_VALIDATION_STARTED
;
285 gaia_oauth_client_
.reset(
286 new gaia::GaiaOAuthClient(g_browser_process
->system_request_context()));
288 GaiaUrls
* gaia_urls
= GaiaUrls::GetInstance();
289 gaia::OAuthClientInfo client_info
;
290 client_info
.client_id
= gaia_urls
->oauth2_chrome_client_id();
291 client_info
.client_secret
= gaia_urls
->oauth2_chrome_client_secret();
293 gaia_oauth_client_
->RefreshToken(
294 client_info
, refresh_token_
,
295 std::vector
<std::string
>(1, GaiaConstants::kOAuthWrapBridgeUserInfoScope
),
296 max_refresh_token_validation_retries_
, this);
299 void DeviceOAuth2TokenServiceDelegate::FlushTokenSaveCallbacks(bool result
) {
300 std::vector
<StatusCallback
> callbacks
;
301 callbacks
.swap(token_save_callbacks_
);
302 for (std::vector
<StatusCallback
>::iterator
callback(callbacks
.begin());
303 callback
!= callbacks
.end(); ++callback
) {
304 if (!callback
->is_null())
305 callback
->Run(result
);
309 void DeviceOAuth2TokenServiceDelegate::RequestValidation() {
310 validation_requested_
= true;
313 void DeviceOAuth2TokenServiceDelegate::SetValidationStatusDelegate(
314 ValidationStatusDelegate
* delegate
) {
315 validation_status_delegate_
= delegate
;
318 void DeviceOAuth2TokenServiceDelegate::ReportServiceError(
319 GoogleServiceAuthError::State error
) {
320 if (validation_status_delegate_
) {
321 validation_status_delegate_
->OnValidationCompleted(error
);
325 } // namespace chromeos