Update V8 to version 4.7.21.
[chromium-blink-merge.git] / chrome / browser / signin / mutable_profile_oauth2_token_service_delegate.cc
blobbe1a38aee1b7ea7d972a96a90d5c36c9c24c2930
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/signin/mutable_profile_oauth2_token_service_delegate.h"
7 #include "base/profiler/scoped_tracker.h"
8 #include "components/signin/core/browser/signin_client.h"
9 #include "components/signin/core/browser/signin_metrics.h"
10 #include "components/signin/core/browser/webdata/token_web_data.h"
11 #include "components/webdata/common/web_data_service_base.h"
12 #include "google_apis/gaia/gaia_auth_fetcher.h"
13 #include "google_apis/gaia/gaia_auth_util.h"
14 #include "google_apis/gaia/gaia_constants.h"
15 #include "google_apis/gaia/oauth2_access_token_fetcher_immediate_error.h"
16 #include "google_apis/gaia/oauth2_access_token_fetcher_impl.h"
17 #include "net/url_request/url_request_context_getter.h"
19 namespace {
21 const char kAccountIdPrefix[] = "AccountId-";
22 const size_t kAccountIdPrefixLength = 10;
24 std::string ApplyAccountIdPrefix(const std::string& account_id) {
25 return kAccountIdPrefix + account_id;
28 bool IsLegacyRefreshTokenId(const std::string& service_id) {
29 return service_id == GaiaConstants::kGaiaOAuth2LoginRefreshToken;
32 bool IsLegacyServiceId(const std::string& account_id) {
33 return account_id.compare(0u, kAccountIdPrefixLength, kAccountIdPrefix) != 0;
36 std::string RemoveAccountIdPrefix(const std::string& prefixed_account_id) {
37 return prefixed_account_id.substr(kAccountIdPrefixLength);
40 } // namespace
42 // This class sends a request to GAIA to revoke the given refresh token from
43 // the server. This is a best effort attempt only. This class deletes itself
44 // when done sucessfully or otherwise.
45 class MutableProfileOAuth2TokenServiceDelegate::RevokeServerRefreshToken
46 : public GaiaAuthConsumer {
47 public:
48 RevokeServerRefreshToken(
49 MutableProfileOAuth2TokenServiceDelegate* token_service_delegate,
50 const std::string& account_id);
51 ~RevokeServerRefreshToken() override;
53 private:
54 // GaiaAuthConsumer overrides:
55 void OnOAuth2RevokeTokenCompleted() override;
57 MutableProfileOAuth2TokenServiceDelegate* token_service_delegate_;
58 GaiaAuthFetcher fetcher_;
60 DISALLOW_COPY_AND_ASSIGN(RevokeServerRefreshToken);
63 MutableProfileOAuth2TokenServiceDelegate::RevokeServerRefreshToken::
64 RevokeServerRefreshToken(
65 MutableProfileOAuth2TokenServiceDelegate* token_service_delegate,
66 const std::string& refresh_token)
67 : token_service_delegate_(token_service_delegate),
68 fetcher_(this,
69 GaiaConstants::kChromeSource,
70 token_service_delegate_->GetRequestContext()) {
71 fetcher_.StartRevokeOAuth2Token(refresh_token);
74 MutableProfileOAuth2TokenServiceDelegate::RevokeServerRefreshToken::
75 ~RevokeServerRefreshToken() {
78 void MutableProfileOAuth2TokenServiceDelegate::RevokeServerRefreshToken::
79 OnOAuth2RevokeTokenCompleted() {
80 // |this| pointer will be deleted when removed from the vector, so don't
81 // access any members after call to erase().
82 token_service_delegate_->server_revokes_.erase(
83 std::find(token_service_delegate_->server_revokes_.begin(),
84 token_service_delegate_->server_revokes_.end(), this));
87 MutableProfileOAuth2TokenServiceDelegate::AccountStatus::AccountStatus(
88 SigninErrorController* signin_error_controller,
89 const std::string& account_id,
90 const std::string& refresh_token)
91 : signin_error_controller_(signin_error_controller),
92 account_id_(account_id),
93 refresh_token_(refresh_token),
94 last_auth_error_(GoogleServiceAuthError::NONE) {
95 DCHECK(signin_error_controller_);
96 DCHECK(!account_id_.empty());
97 signin_error_controller_->AddProvider(this);
100 MutableProfileOAuth2TokenServiceDelegate::AccountStatus::~AccountStatus() {
101 signin_error_controller_->RemoveProvider(this);
104 void MutableProfileOAuth2TokenServiceDelegate::AccountStatus::SetLastAuthError(
105 const GoogleServiceAuthError& error) {
106 if (error.state() != last_auth_error_.state()) {
107 last_auth_error_ = error;
108 signin_error_controller_->AuthStatusChanged();
112 std::string
113 MutableProfileOAuth2TokenServiceDelegate::AccountStatus::GetAccountId() const {
114 return account_id_;
117 GoogleServiceAuthError
118 MutableProfileOAuth2TokenServiceDelegate::AccountStatus::GetAuthStatus() const {
119 return last_auth_error_;
122 MutableProfileOAuth2TokenServiceDelegate::
123 MutableProfileOAuth2TokenServiceDelegate(
124 SigninClient* client,
125 SigninErrorController* signin_error_controller,
126 AccountTrackerService* account_tracker_service)
127 : web_data_service_request_(0),
128 backoff_entry_(&backoff_policy_),
129 backoff_error_(GoogleServiceAuthError::NONE),
130 client_(client),
131 signin_error_controller_(signin_error_controller),
132 account_tracker_service_(account_tracker_service) {
133 VLOG(1) << "MutablePO2TS::MutablePO2TS";
134 DCHECK(client);
135 DCHECK(signin_error_controller);
136 // It's okay to fill the backoff policy after being used in construction.
137 backoff_policy_.num_errors_to_ignore = 0;
138 backoff_policy_.initial_delay_ms = 1000;
139 backoff_policy_.multiply_factor = 2.0;
140 backoff_policy_.jitter_factor = 0.2;
141 backoff_policy_.maximum_backoff_ms = 15 * 60 * 1000;
142 backoff_policy_.entry_lifetime_ms = -1;
143 backoff_policy_.always_use_initial_delay = false;
146 MutableProfileOAuth2TokenServiceDelegate::
147 ~MutableProfileOAuth2TokenServiceDelegate() {
148 VLOG(1) << "MutablePO2TS::~MutablePO2TS";
149 DCHECK(server_revokes_.empty());
152 OAuth2AccessTokenFetcher*
153 MutableProfileOAuth2TokenServiceDelegate::CreateAccessTokenFetcher(
154 const std::string& account_id,
155 net::URLRequestContextGetter* getter,
156 OAuth2AccessTokenConsumer* consumer) {
157 ValidateAccountId(account_id);
158 // check whether the account has persistent error.
159 if (refresh_tokens_[account_id]->GetAuthStatus().IsPersistentError()) {
160 VLOG(1) << "Request for token has been rejected due to persistent error #"
161 << refresh_tokens_[account_id]->GetAuthStatus().state();
162 return new OAuth2AccessTokenFetcherImmediateError(
163 consumer, refresh_tokens_[account_id]->GetAuthStatus());
165 if (backoff_entry_.ShouldRejectRequest()) {
166 VLOG(1) << "Request for token has been rejected due to backoff rules from"
167 << " previous error #" << backoff_error_.state();
168 return new OAuth2AccessTokenFetcherImmediateError(consumer, backoff_error_);
170 std::string refresh_token = GetRefreshToken(account_id);
171 DCHECK(!refresh_token.empty());
172 return new OAuth2AccessTokenFetcherImpl(consumer, getter, refresh_token);
175 bool MutableProfileOAuth2TokenServiceDelegate::RefreshTokenHasError(
176 const std::string& account_id) const {
177 auto it = refresh_tokens_.find(account_id);
178 return it == refresh_tokens_.end() ? false
179 : IsError(it->second->GetAuthStatus());
182 void MutableProfileOAuth2TokenServiceDelegate::UpdateAuthError(
183 const std::string& account_id,
184 const GoogleServiceAuthError& error) {
185 VLOG(1) << "MutablePO2TS::UpdateAuthError. Error: " << error.state();
186 backoff_entry_.InformOfRequest(!error.IsTransientError());
187 ValidateAccountId(account_id);
189 // Do not report connection errors as these are not actually auth errors.
190 // We also want to avoid masking a "real" auth error just because we
191 // subsequently get a transient network error. We do keep it around though
192 // to report for future requests being denied for "backoff" reasons.
193 if (error.IsTransientError()) {
194 backoff_error_ = error;
195 return;
198 if (refresh_tokens_.count(account_id) == 0) {
199 // This could happen if the preferences have been corrupted (see
200 // http://crbug.com/321370). In a Debug build that would be a bug, but in a
201 // Release build we want to deal with it gracefully.
202 NOTREACHED();
203 return;
205 refresh_tokens_[account_id]->SetLastAuthError(error);
208 bool MutableProfileOAuth2TokenServiceDelegate::RefreshTokenIsAvailable(
209 const std::string& account_id) const {
210 VLOG(1) << "MutablePO2TS::RefreshTokenIsAvailable";
211 return !GetRefreshToken(account_id).empty();
214 std::string MutableProfileOAuth2TokenServiceDelegate::GetRefreshToken(
215 const std::string& account_id) const {
216 AccountStatusMap::const_iterator iter = refresh_tokens_.find(account_id);
217 if (iter != refresh_tokens_.end())
218 return iter->second->refresh_token();
219 return std::string();
222 std::vector<std::string>
223 MutableProfileOAuth2TokenServiceDelegate::GetAccounts() {
224 std::vector<std::string> account_ids;
225 for (auto& token : refresh_tokens_) {
226 account_ids.push_back(token.first);
228 return account_ids;
231 net::URLRequestContextGetter*
232 MutableProfileOAuth2TokenServiceDelegate::GetRequestContext() const {
233 return client_->GetURLRequestContext();
236 void MutableProfileOAuth2TokenServiceDelegate::LoadCredentials(
237 const std::string& primary_account_id) {
238 DCHECK(!primary_account_id.empty());
239 ValidateAccountId(primary_account_id);
240 DCHECK(loading_primary_account_id_.empty());
241 DCHECK_EQ(0, web_data_service_request_);
243 refresh_tokens_.clear();
245 // If the account_id is an email address, then canonicalize it. This
246 // is to support legacy account_ids, and will not be needed after
247 // switching to gaia-ids.
248 if (primary_account_id.find('@') != std::string::npos) {
249 loading_primary_account_id_ = gaia::CanonicalizeEmail(primary_account_id);
250 } else {
251 loading_primary_account_id_ = primary_account_id;
254 scoped_refptr<TokenWebData> token_web_data = client_->GetDatabase();
255 if (token_web_data.get())
256 web_data_service_request_ = token_web_data->GetAllTokens(this);
259 void MutableProfileOAuth2TokenServiceDelegate::OnWebDataServiceRequestDone(
260 WebDataServiceBase::Handle handle,
261 const WDTypedResult* result) {
262 VLOG(1) << "MutablePO2TS::OnWebDataServiceRequestDone. Result type: "
263 << (result == nullptr ? -1 : (int)result->GetType());
265 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
266 // fixed.
267 tracked_objects::ScopedTracker tracking_profile(
268 FROM_HERE_WITH_EXPLICIT_FUNCTION(
269 "422460 MutableProfileOAuth2Token...::OnWebDataServiceRequestDone"));
271 DCHECK_EQ(web_data_service_request_, handle);
272 web_data_service_request_ = 0;
274 if (result) {
275 DCHECK(result->GetType() == TOKEN_RESULT);
276 const WDResult<std::map<std::string, std::string>>* token_result =
277 static_cast<const WDResult<std::map<std::string, std::string>>*>(
278 result);
279 LoadAllCredentialsIntoMemory(token_result->GetValue());
282 // Make sure that we have an entry for |loading_primary_account_id_| in the
283 // map. The entry could be missing if there is a corruption in the token DB
284 // while this profile is connected to an account.
285 DCHECK(!loading_primary_account_id_.empty());
286 if (refresh_tokens_.count(loading_primary_account_id_) == 0) {
287 refresh_tokens_[loading_primary_account_id_].reset(new AccountStatus(
288 signin_error_controller_, loading_primary_account_id_, std::string()));
291 // If we don't have a refresh token for a known account, signal an error.
292 for (auto& token : refresh_tokens_) {
293 if (!RefreshTokenIsAvailable(token.first)) {
294 UpdateAuthError(token.first,
295 GoogleServiceAuthError(
296 GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
297 break;
301 loading_primary_account_id_.clear();
304 void MutableProfileOAuth2TokenServiceDelegate::LoadAllCredentialsIntoMemory(
305 const std::map<std::string, std::string>& db_tokens) {
306 std::string old_login_token;
309 ScopedBatchChange batch(this);
311 VLOG(1) << "MutablePO2TS::LoadAllCredentialsIntoMemory; "
312 << db_tokens.size() << " Credential(s).";
313 AccountTrackerService::AccountIdMigrationState migration_state =
314 account_tracker_service_->GetMigrationState();
315 for (std::map<std::string, std::string>::const_iterator iter =
316 db_tokens.begin();
317 iter != db_tokens.end(); ++iter) {
318 std::string prefixed_account_id = iter->first;
319 std::string refresh_token = iter->second;
321 if (IsLegacyRefreshTokenId(prefixed_account_id) && !refresh_token.empty())
322 old_login_token = refresh_token;
324 if (IsLegacyServiceId(prefixed_account_id)) {
325 scoped_refptr<TokenWebData> token_web_data = client_->GetDatabase();
326 if (token_web_data.get())
327 token_web_data->RemoveTokenForService(prefixed_account_id);
328 } else {
329 DCHECK(!refresh_token.empty());
330 std::string account_id = RemoveAccountIdPrefix(prefixed_account_id);
332 if (migration_state == AccountTrackerService::MIGRATION_IN_PROGRESS) {
333 // Migrate to gaia-ids.
334 AccountInfo account_info =
335 account_tracker_service_->FindAccountInfoByEmail(account_id);
336 // |account_info.gaia| could be empty if |account_id| is already gaia
337 // id. This could happen if the chrome was closed in the middle of
338 // migration.
339 if (!account_info.gaia.empty()) {
340 PersistCredentials(account_info.gaia, refresh_token);
341 ClearPersistedCredentials(account_id);
342 account_id = account_info.gaia;
345 // Skip duplicate accounts, this could happen if migration was
346 // crashed in the middle.
347 if (refresh_tokens_.count(account_id) != 0)
348 continue;
351 // If the account_id is an email address, then canonicalize it. This
352 // is to support legacy account_ids, and will not be needed after
353 // switching to gaia-ids.
354 if (account_id.find('@') != std::string::npos) {
355 // If the canonical account id is not the same as the loaded
356 // account id, make sure not to overwrite a refresh token from
357 // a canonical version. If no canonical version was loaded, then
358 // re-persist this refresh token with the canonical account id.
359 std::string canon_account_id = gaia::CanonicalizeEmail(account_id);
360 if (canon_account_id != account_id) {
361 ClearPersistedCredentials(account_id);
362 if (db_tokens.count(ApplyAccountIdPrefix(canon_account_id)) == 0)
363 PersistCredentials(canon_account_id, refresh_token);
366 account_id = canon_account_id;
369 refresh_tokens_[account_id].reset(new AccountStatus(
370 signin_error_controller_, account_id, refresh_token));
371 FireRefreshTokenAvailable(account_id);
375 if (!old_login_token.empty()) {
376 DCHECK(!loading_primary_account_id_.empty());
377 if (refresh_tokens_.count(loading_primary_account_id_) == 0)
378 UpdateCredentials(loading_primary_account_id_, old_login_token);
382 FireRefreshTokensLoaded();
385 void MutableProfileOAuth2TokenServiceDelegate::UpdateCredentials(
386 const std::string& account_id,
387 const std::string& refresh_token) {
388 DCHECK(thread_checker_.CalledOnValidThread());
389 DCHECK(!account_id.empty());
390 DCHECK(!refresh_token.empty());
391 ValidateAccountId(account_id);
393 signin_metrics::LogSigninAddAccount();
395 bool refresh_token_present = refresh_tokens_.count(account_id) > 0;
396 if (!refresh_token_present ||
397 refresh_tokens_[account_id]->refresh_token() != refresh_token) {
398 ScopedBatchChange batch(this);
400 // If token present, and different from the new one, cancel its requests,
401 // and clear the entries in cache related to that account.
402 if (refresh_token_present) {
403 VLOG(1) << "MutablePO2TS::UpdateCredentials; Refresh Token was present. "
404 << "account_id=" << account_id;
406 refresh_tokens_[account_id]->set_refresh_token(refresh_token);
407 } else {
408 VLOG(1) << "MutablePO2TS::UpdateCredentials; Refresh Token was absent. "
409 << "account_id=" << account_id;
410 refresh_tokens_[account_id].reset(new AccountStatus(
411 signin_error_controller_, account_id, refresh_token));
414 // Save the token in memory and in persistent store.
415 PersistCredentials(account_id, refresh_token);
417 UpdateAuthError(account_id, GoogleServiceAuthError::AuthErrorNone());
418 FireRefreshTokenAvailable(account_id);
422 void MutableProfileOAuth2TokenServiceDelegate::PersistCredentials(
423 const std::string& account_id,
424 const std::string& refresh_token) {
425 scoped_refptr<TokenWebData> token_web_data = client_->GetDatabase();
426 if (token_web_data.get()) {
427 VLOG(1) << "MutablePO2TS::PersistCredentials for account_id=" << account_id;
428 token_web_data->SetTokenForService(ApplyAccountIdPrefix(account_id),
429 refresh_token);
433 void MutableProfileOAuth2TokenServiceDelegate::RevokeAllCredentials() {
434 if (!client_->CanRevokeCredentials())
435 return;
436 DCHECK(thread_checker_.CalledOnValidThread());
438 ScopedBatchChange batch(this);
440 VLOG(1) << "MutablePO2TS::RevokeAllCredentials";
441 CancelWebTokenFetch();
442 AccountStatusMap tokens = refresh_tokens_;
443 for (auto& token : tokens)
444 RevokeCredentials(token.first);
446 DCHECK_EQ(0u, refresh_tokens_.size());
448 // Make sure all tokens are removed.
449 scoped_refptr<TokenWebData> token_web_data = client_->GetDatabase();
450 if (token_web_data.get())
451 token_web_data->RemoveAllTokens();
454 void MutableProfileOAuth2TokenServiceDelegate::RevokeCredentials(
455 const std::string& account_id) {
456 ValidateAccountId(account_id);
457 DCHECK(thread_checker_.CalledOnValidThread());
459 if (refresh_tokens_.count(account_id) > 0) {
460 VLOG(1) << "MutablePO2TS::RevokeCredentials for account_id=" << account_id;
461 ScopedBatchChange batch(this);
462 RevokeCredentialsOnServer(refresh_tokens_[account_id]->refresh_token());
463 refresh_tokens_.erase(account_id);
464 ClearPersistedCredentials(account_id);
465 FireRefreshTokenRevoked(account_id);
469 void MutableProfileOAuth2TokenServiceDelegate::ClearPersistedCredentials(
470 const std::string& account_id) {
471 scoped_refptr<TokenWebData> token_web_data = client_->GetDatabase();
472 if (token_web_data.get()) {
473 VLOG(1) << "MutablePO2TS::ClearPersistedCredentials for account_id="
474 << account_id;
475 token_web_data->RemoveTokenForService(ApplyAccountIdPrefix(account_id));
479 void MutableProfileOAuth2TokenServiceDelegate::RevokeCredentialsOnServer(
480 const std::string& refresh_token) {
481 // Keep track or all server revoke requests. This way they can be deleted
482 // before the token service is shutdown and won't outlive the profile.
483 server_revokes_.push_back(new RevokeServerRefreshToken(this, refresh_token));
486 void MutableProfileOAuth2TokenServiceDelegate::CancelWebTokenFetch() {
487 if (web_data_service_request_ != 0) {
488 scoped_refptr<TokenWebData> token_web_data = client_->GetDatabase();
489 DCHECK(token_web_data.get());
490 token_web_data->CancelRequest(web_data_service_request_);
491 web_data_service_request_ = 0;
495 void MutableProfileOAuth2TokenServiceDelegate::Shutdown() {
496 VLOG(1) << "MutablePO2TS::Shutdown";
497 server_revokes_.clear();
498 CancelWebTokenFetch();
499 refresh_tokens_.clear();