Only grant permissions to new extensions from sync if they have the expected version
[chromium-blink-merge.git] / chrome / browser / signin / mutable_profile_oauth2_token_service_delegate.cc
blob1b884553be34c232faf0a8d3c12d90fe0dde406f
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/signin/core/common/profile_management_switches.h"
12 #include "components/webdata/common/web_data_service_base.h"
13 #include "google_apis/gaia/gaia_auth_fetcher.h"
14 #include "google_apis/gaia/gaia_auth_util.h"
15 #include "google_apis/gaia/gaia_constants.h"
16 #include "google_apis/gaia/oauth2_access_token_fetcher_immediate_error.h"
17 #include "google_apis/gaia/oauth2_access_token_fetcher_impl.h"
18 #include "net/url_request/url_request_context_getter.h"
20 namespace {
22 const char kAccountIdPrefix[] = "AccountId-";
23 const size_t kAccountIdPrefixLength = 10;
25 std::string ApplyAccountIdPrefix(const std::string& account_id) {
26 return kAccountIdPrefix + account_id;
29 bool IsLegacyRefreshTokenId(const std::string& service_id) {
30 return service_id == GaiaConstants::kGaiaOAuth2LoginRefreshToken;
33 bool IsLegacyServiceId(const std::string& account_id) {
34 return account_id.compare(0u, kAccountIdPrefixLength, kAccountIdPrefix) != 0;
37 std::string RemoveAccountIdPrefix(const std::string& prefixed_account_id) {
38 return prefixed_account_id.substr(kAccountIdPrefixLength);
41 } // namespace
43 // This class sends a request to GAIA to revoke the given refresh token from
44 // the server. This is a best effort attempt only. This class deletes itself
45 // when done sucessfully or otherwise.
46 class MutableProfileOAuth2TokenServiceDelegate::RevokeServerRefreshToken
47 : public GaiaAuthConsumer {
48 public:
49 RevokeServerRefreshToken(
50 MutableProfileOAuth2TokenServiceDelegate* token_service_delegate,
51 const std::string& account_id);
52 ~RevokeServerRefreshToken() override;
54 private:
55 // GaiaAuthConsumer overrides:
56 void OnOAuth2RevokeTokenCompleted() override;
58 MutableProfileOAuth2TokenServiceDelegate* token_service_delegate_;
59 GaiaAuthFetcher fetcher_;
61 DISALLOW_COPY_AND_ASSIGN(RevokeServerRefreshToken);
64 MutableProfileOAuth2TokenServiceDelegate::RevokeServerRefreshToken::
65 RevokeServerRefreshToken(
66 MutableProfileOAuth2TokenServiceDelegate* token_service_delegate,
67 const std::string& refresh_token)
68 : token_service_delegate_(token_service_delegate),
69 fetcher_(this,
70 GaiaConstants::kChromeSource,
71 token_service_delegate_->GetRequestContext()) {
72 fetcher_.StartRevokeOAuth2Token(refresh_token);
75 MutableProfileOAuth2TokenServiceDelegate::RevokeServerRefreshToken::
76 ~RevokeServerRefreshToken() {
79 void MutableProfileOAuth2TokenServiceDelegate::RevokeServerRefreshToken::
80 OnOAuth2RevokeTokenCompleted() {
81 // |this| pointer will be deleted when removed from the vector, so don't
82 // access any members after call to erase().
83 token_service_delegate_->server_revokes_.erase(
84 std::find(token_service_delegate_->server_revokes_.begin(),
85 token_service_delegate_->server_revokes_.end(), this));
88 MutableProfileOAuth2TokenServiceDelegate::AccountStatus::AccountStatus(
89 SigninErrorController* signin_error_controller,
90 const std::string& account_id,
91 const std::string& refresh_token)
92 : signin_error_controller_(signin_error_controller),
93 account_id_(account_id),
94 refresh_token_(refresh_token),
95 last_auth_error_(GoogleServiceAuthError::NONE) {
96 DCHECK(signin_error_controller_);
97 DCHECK(!account_id_.empty());
98 signin_error_controller_->AddProvider(this);
101 MutableProfileOAuth2TokenServiceDelegate::AccountStatus::~AccountStatus() {
102 signin_error_controller_->RemoveProvider(this);
105 void MutableProfileOAuth2TokenServiceDelegate::AccountStatus::SetLastAuthError(
106 const GoogleServiceAuthError& error) {
107 if (error.state() != last_auth_error_.state()) {
108 last_auth_error_ = error;
109 signin_error_controller_->AuthStatusChanged();
113 std::string
114 MutableProfileOAuth2TokenServiceDelegate::AccountStatus::GetAccountId() const {
115 return account_id_;
118 GoogleServiceAuthError
119 MutableProfileOAuth2TokenServiceDelegate::AccountStatus::GetAuthStatus() const {
120 return last_auth_error_;
123 MutableProfileOAuth2TokenServiceDelegate::
124 MutableProfileOAuth2TokenServiceDelegate(
125 SigninClient* client,
126 SigninErrorController* signin_error_controller,
127 AccountTrackerService* account_tracker_service)
128 : web_data_service_request_(0),
129 backoff_entry_(&backoff_policy_),
130 backoff_error_(GoogleServiceAuthError::NONE),
131 client_(client),
132 signin_error_controller_(signin_error_controller),
133 account_tracker_service_(account_tracker_service) {
134 VLOG(1) << "MutablePO2TS::MutablePO2TS";
135 DCHECK(client);
136 DCHECK(signin_error_controller);
137 // It's okay to fill the backoff policy after being used in construction.
138 backoff_policy_.num_errors_to_ignore = 0;
139 backoff_policy_.initial_delay_ms = 1000;
140 backoff_policy_.multiply_factor = 2.0;
141 backoff_policy_.jitter_factor = 0.2;
142 backoff_policy_.maximum_backoff_ms = 15 * 60 * 1000;
143 backoff_policy_.entry_lifetime_ms = -1;
144 backoff_policy_.always_use_initial_delay = false;
147 MutableProfileOAuth2TokenServiceDelegate::
148 ~MutableProfileOAuth2TokenServiceDelegate() {
149 VLOG(1) << "MutablePO2TS::~MutablePO2TS";
150 DCHECK(server_revokes_.empty());
153 OAuth2AccessTokenFetcher*
154 MutableProfileOAuth2TokenServiceDelegate::CreateAccessTokenFetcher(
155 const std::string& account_id,
156 net::URLRequestContextGetter* getter,
157 OAuth2AccessTokenConsumer* consumer) {
158 ValidateAccountId(account_id);
159 // check whether the account has persistent error.
160 if (refresh_tokens_[account_id]->GetAuthStatus().IsPersistentError()) {
161 VLOG(1) << "Request for token has been rejected due to persistent error #"
162 << refresh_tokens_[account_id]->GetAuthStatus().state();
163 return new OAuth2AccessTokenFetcherImmediateError(
164 consumer, refresh_tokens_[account_id]->GetAuthStatus());
166 if (backoff_entry_.ShouldRejectRequest()) {
167 VLOG(1) << "Request for token has been rejected due to backoff rules from"
168 << " previous error #" << backoff_error_.state();
169 return new OAuth2AccessTokenFetcherImmediateError(consumer, backoff_error_);
171 std::string refresh_token = GetRefreshToken(account_id);
172 DCHECK(!refresh_token.empty());
173 return new OAuth2AccessTokenFetcherImpl(consumer, getter, refresh_token);
176 bool MutableProfileOAuth2TokenServiceDelegate::RefreshTokenHasError(
177 const std::string& account_id) const {
178 auto it = refresh_tokens_.find(account_id);
179 return it == refresh_tokens_.end() ? false
180 : IsError(it->second->GetAuthStatus());
183 void MutableProfileOAuth2TokenServiceDelegate::UpdateAuthError(
184 const std::string& account_id,
185 const GoogleServiceAuthError& error) {
186 VLOG(1) << "MutablePO2TS::UpdateAuthError. Error: " << error.state();
187 backoff_entry_.InformOfRequest(!error.IsTransientError());
188 ValidateAccountId(account_id);
190 // Do not report connection errors as these are not actually auth errors.
191 // We also want to avoid masking a "real" auth error just because we
192 // subsequently get a transient network error. We do keep it around though
193 // to report for future requests being denied for "backoff" reasons.
194 if (error.IsTransientError()) {
195 backoff_error_ = error;
196 return;
199 if (refresh_tokens_.count(account_id) == 0) {
200 // This could happen if the preferences have been corrupted (see
201 // http://crbug.com/321370). In a Debug build that would be a bug, but in a
202 // Release build we want to deal with it gracefully.
203 NOTREACHED();
204 return;
206 refresh_tokens_[account_id]->SetLastAuthError(error);
209 bool MutableProfileOAuth2TokenServiceDelegate::RefreshTokenIsAvailable(
210 const std::string& account_id) const {
211 VLOG(1) << "MutablePO2TS::RefreshTokenIsAvailable";
212 return !GetRefreshToken(account_id).empty();
215 std::string MutableProfileOAuth2TokenServiceDelegate::GetRefreshToken(
216 const std::string& account_id) const {
217 AccountStatusMap::const_iterator iter = refresh_tokens_.find(account_id);
218 if (iter != refresh_tokens_.end())
219 return iter->second->refresh_token();
220 return std::string();
223 std::vector<std::string>
224 MutableProfileOAuth2TokenServiceDelegate::GetAccounts() {
225 std::vector<std::string> account_ids;
226 for (auto& token : refresh_tokens_) {
227 account_ids.push_back(token.first);
229 return account_ids;
232 net::URLRequestContextGetter*
233 MutableProfileOAuth2TokenServiceDelegate::GetRequestContext() const {
234 return client_->GetURLRequestContext();
237 void MutableProfileOAuth2TokenServiceDelegate::LoadCredentials(
238 const std::string& primary_account_id) {
239 DCHECK(!primary_account_id.empty());
240 ValidateAccountId(primary_account_id);
241 DCHECK(loading_primary_account_id_.empty());
242 DCHECK_EQ(0, web_data_service_request_);
244 refresh_tokens_.clear();
246 // If the account_id is an email address, then canonicalize it. This
247 // is to support legacy account_ids, and will not be needed after
248 // switching to gaia-ids.
249 if (primary_account_id.find('@') != std::string::npos) {
250 loading_primary_account_id_ = gaia::CanonicalizeEmail(primary_account_id);
251 } else {
252 loading_primary_account_id_ = primary_account_id;
255 scoped_refptr<TokenWebData> token_web_data = client_->GetDatabase();
256 if (token_web_data.get())
257 web_data_service_request_ = token_web_data->GetAllTokens(this);
260 void MutableProfileOAuth2TokenServiceDelegate::OnWebDataServiceRequestDone(
261 WebDataServiceBase::Handle handle,
262 const WDTypedResult* result) {
263 VLOG(1) << "MutablePO2TS::OnWebDataServiceRequestDone. Result type: "
264 << (result == nullptr ? -1 : (int)result->GetType());
266 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
267 // fixed.
268 tracked_objects::ScopedTracker tracking_profile(
269 FROM_HERE_WITH_EXPLICIT_FUNCTION(
270 "422460 MutableProfileOAuth2Token...::OnWebDataServiceRequestDone"));
272 DCHECK_EQ(web_data_service_request_, handle);
273 web_data_service_request_ = 0;
275 if (result) {
276 DCHECK(result->GetType() == TOKEN_RESULT);
277 const WDResult<std::map<std::string, std::string>>* token_result =
278 static_cast<const WDResult<std::map<std::string, std::string>>*>(
279 result);
280 LoadAllCredentialsIntoMemory(token_result->GetValue());
283 // Make sure that we have an entry for |loading_primary_account_id_| in the
284 // map. The entry could be missing if there is a corruption in the token DB
285 // while this profile is connected to an account.
286 DCHECK(!loading_primary_account_id_.empty());
287 if (refresh_tokens_.count(loading_primary_account_id_) == 0) {
288 refresh_tokens_[loading_primary_account_id_].reset(new AccountStatus(
289 signin_error_controller_, loading_primary_account_id_, std::string()));
292 // If we don't have a refresh token for a known account, signal an error.
293 for (auto& token : refresh_tokens_) {
294 if (!RefreshTokenIsAvailable(token.first)) {
295 UpdateAuthError(token.first,
296 GoogleServiceAuthError(
297 GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
298 break;
302 loading_primary_account_id_.clear();
305 void MutableProfileOAuth2TokenServiceDelegate::LoadAllCredentialsIntoMemory(
306 const std::map<std::string, std::string>& db_tokens) {
307 std::string old_login_token;
310 ScopedBatchChange batch(this);
312 VLOG(1) << "MutablePO2TS::LoadAllCredentialsIntoMemory; "
313 << db_tokens.size() << " Credential(s).";
314 AccountTrackerService::AccountIdMigrationState migration_state =
315 account_tracker_service_->GetMigrationState();
316 for (std::map<std::string, std::string>::const_iterator iter =
317 db_tokens.begin();
318 iter != db_tokens.end(); ++iter) {
319 std::string prefixed_account_id = iter->first;
320 std::string refresh_token = iter->second;
322 if (IsLegacyRefreshTokenId(prefixed_account_id) && !refresh_token.empty())
323 old_login_token = refresh_token;
325 if (IsLegacyServiceId(prefixed_account_id)) {
326 scoped_refptr<TokenWebData> token_web_data = client_->GetDatabase();
327 if (token_web_data.get())
328 token_web_data->RemoveTokenForService(prefixed_account_id);
329 } else {
330 DCHECK(!refresh_token.empty());
331 std::string account_id = RemoveAccountIdPrefix(prefixed_account_id);
333 if (migration_state == AccountTrackerService::MIGRATION_IN_PROGRESS) {
334 // Migrate to gaia-ids.
335 AccountInfo account_info =
336 account_tracker_service_->FindAccountInfoByEmail(account_id);
337 // |account_info.gaia| could be empty if |account_id| is already gaia
338 // id. This could happen if the chrome was closed in the middle of
339 // migration.
340 if (!account_info.gaia.empty()) {
341 PersistCredentials(account_info.gaia, refresh_token);
342 ClearPersistedCredentials(account_id);
343 account_id = account_info.gaia;
346 // Skip duplicate accounts, this could happen if migration was
347 // crashed in the middle.
348 if (refresh_tokens_.count(account_id) != 0)
349 continue;
352 // If the account_id is an email address, then canonicalize it. This
353 // is to support legacy account_ids, and will not be needed after
354 // switching to gaia-ids.
355 if (account_id.find('@') != std::string::npos) {
356 // If the canonical account id is not the same as the loaded
357 // account id, make sure not to overwrite a refresh token from
358 // a canonical version. If no canonical version was loaded, then
359 // re-persist this refresh token with the canonical account id.
360 std::string canon_account_id = gaia::CanonicalizeEmail(account_id);
361 if (canon_account_id != account_id) {
362 ClearPersistedCredentials(account_id);
363 if (db_tokens.count(ApplyAccountIdPrefix(canon_account_id)) == 0)
364 PersistCredentials(canon_account_id, refresh_token);
367 account_id = canon_account_id;
370 // Only load secondary accounts when account consistency is enabled.
371 if (switches::IsEnableAccountConsistency() ||
372 account_id == loading_primary_account_id_) {
373 refresh_tokens_[account_id].reset(new AccountStatus(
374 signin_error_controller_, account_id, refresh_token));
375 FireRefreshTokenAvailable(account_id);
376 } else {
377 RevokeCredentialsOnServer(refresh_token);
378 ClearPersistedCredentials(account_id);
379 FireRefreshTokenRevoked(account_id);
384 if (!old_login_token.empty()) {
385 DCHECK(!loading_primary_account_id_.empty());
386 if (refresh_tokens_.count(loading_primary_account_id_) == 0)
387 UpdateCredentials(loading_primary_account_id_, old_login_token);
391 FireRefreshTokensLoaded();
394 void MutableProfileOAuth2TokenServiceDelegate::UpdateCredentials(
395 const std::string& account_id,
396 const std::string& refresh_token) {
397 DCHECK(thread_checker_.CalledOnValidThread());
398 DCHECK(!account_id.empty());
399 DCHECK(!refresh_token.empty());
400 ValidateAccountId(account_id);
402 signin_metrics::LogSigninAddAccount();
404 bool refresh_token_present = refresh_tokens_.count(account_id) > 0;
405 if (!refresh_token_present ||
406 refresh_tokens_[account_id]->refresh_token() != refresh_token) {
407 ScopedBatchChange batch(this);
409 // If token present, and different from the new one, cancel its requests,
410 // and clear the entries in cache related to that account.
411 if (refresh_token_present) {
412 VLOG(1) << "MutablePO2TS::UpdateCredentials; Refresh Token was present. "
413 << "account_id=" << account_id;
415 refresh_tokens_[account_id]->set_refresh_token(refresh_token);
416 } else {
417 VLOG(1) << "MutablePO2TS::UpdateCredentials; Refresh Token was absent. "
418 << "account_id=" << account_id;
419 refresh_tokens_[account_id].reset(new AccountStatus(
420 signin_error_controller_, account_id, refresh_token));
423 // Save the token in memory and in persistent store.
424 PersistCredentials(account_id, refresh_token);
426 UpdateAuthError(account_id, GoogleServiceAuthError::AuthErrorNone());
427 FireRefreshTokenAvailable(account_id);
431 void MutableProfileOAuth2TokenServiceDelegate::PersistCredentials(
432 const std::string& account_id,
433 const std::string& refresh_token) {
434 scoped_refptr<TokenWebData> token_web_data = client_->GetDatabase();
435 if (token_web_data.get()) {
436 VLOG(1) << "MutablePO2TS::PersistCredentials for account_id=" << account_id;
437 token_web_data->SetTokenForService(ApplyAccountIdPrefix(account_id),
438 refresh_token);
442 void MutableProfileOAuth2TokenServiceDelegate::RevokeAllCredentials() {
443 if (!client_->CanRevokeCredentials())
444 return;
445 DCHECK(thread_checker_.CalledOnValidThread());
447 ScopedBatchChange batch(this);
449 VLOG(1) << "MutablePO2TS::RevokeAllCredentials";
450 CancelWebTokenFetch();
451 AccountStatusMap tokens = refresh_tokens_;
452 for (auto& token : tokens)
453 RevokeCredentials(token.first);
455 DCHECK_EQ(0u, refresh_tokens_.size());
457 // Make sure all tokens are removed.
458 scoped_refptr<TokenWebData> token_web_data = client_->GetDatabase();
459 if (token_web_data.get())
460 token_web_data->RemoveAllTokens();
463 void MutableProfileOAuth2TokenServiceDelegate::RevokeCredentials(
464 const std::string& account_id) {
465 ValidateAccountId(account_id);
466 DCHECK(thread_checker_.CalledOnValidThread());
468 if (refresh_tokens_.count(account_id) > 0) {
469 VLOG(1) << "MutablePO2TS::RevokeCredentials for account_id=" << account_id;
470 ScopedBatchChange batch(this);
471 RevokeCredentialsOnServer(refresh_tokens_[account_id]->refresh_token());
472 refresh_tokens_.erase(account_id);
473 ClearPersistedCredentials(account_id);
474 FireRefreshTokenRevoked(account_id);
478 void MutableProfileOAuth2TokenServiceDelegate::ClearPersistedCredentials(
479 const std::string& account_id) {
480 scoped_refptr<TokenWebData> token_web_data = client_->GetDatabase();
481 if (token_web_data.get()) {
482 VLOG(1) << "MutablePO2TS::ClearPersistedCredentials for account_id="
483 << account_id;
484 token_web_data->RemoveTokenForService(ApplyAccountIdPrefix(account_id));
488 void MutableProfileOAuth2TokenServiceDelegate::RevokeCredentialsOnServer(
489 const std::string& refresh_token) {
490 // Keep track or all server revoke requests. This way they can be deleted
491 // before the token service is shutdown and won't outlive the profile.
492 server_revokes_.push_back(new RevokeServerRefreshToken(this, refresh_token));
495 void MutableProfileOAuth2TokenServiceDelegate::CancelWebTokenFetch() {
496 if (web_data_service_request_ != 0) {
497 scoped_refptr<TokenWebData> token_web_data = client_->GetDatabase();
498 DCHECK(token_web_data.get());
499 token_web_data->CancelRequest(web_data_service_request_);
500 web_data_service_request_ = 0;
504 void MutableProfileOAuth2TokenServiceDelegate::Shutdown() {
505 VLOG(1) << "MutablePO2TS::Shutdown";
506 server_revokes_.clear();
507 CancelWebTokenFetch();
508 refresh_tokens_.clear();