1 // Copyright 2013 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.h"
7 #include "chrome/browser/profiles/profile.h"
8 #include "chrome/browser/webdata/web_data_service_factory.h"
9 #include "components/signin/core/webdata/token_web_data.h"
10 #include "components/webdata/common/web_data_service_base.h"
11 #include "content/public/browser/browser_thread.h"
12 #include "google_apis/gaia/gaia_auth_fetcher.h"
13 #include "google_apis/gaia/gaia_constants.h"
14 #include "google_apis/gaia/google_service_auth_error.h"
15 #include "net/url_request/url_request_context_getter.h"
17 #if defined(ENABLE_MANAGED_USERS)
18 #include "chrome/browser/managed_mode/managed_user_constants.h"
23 const char kAccountIdPrefix[] = "AccountId-";
24 const size_t kAccountIdPrefixLength = 10;
26 std::string ApplyAccountIdPrefix(const std::string& account_id) {
27 return kAccountIdPrefix + account_id;
30 bool IsLegacyRefreshTokenId(const std::string& service_id) {
31 return service_id == GaiaConstants::kGaiaOAuth2LoginRefreshToken;
34 bool IsLegacyServiceId(const std::string& account_id) {
35 return account_id.compare(0u, kAccountIdPrefixLength, kAccountIdPrefix) != 0;
38 std::string RemoveAccountIdPrefix(const std::string& prefixed_account_id) {
39 return prefixed_account_id.substr(kAccountIdPrefixLength);
44 // This class sends a request to GAIA to revoke the given refresh token from
45 // the server. This is a best effort attempt only. This class deletes itself
46 // when done sucessfully or otherwise.
47 class MutableProfileOAuth2TokenService::RevokeServerRefreshToken
48 : public GaiaAuthConsumer {
50 RevokeServerRefreshToken(MutableProfileOAuth2TokenService* token_service,
51 const std::string& account_id);
52 virtual ~RevokeServerRefreshToken();
55 // GaiaAuthConsumer overrides:
56 virtual void OnOAuth2RevokeTokenCompleted() OVERRIDE;
58 MutableProfileOAuth2TokenService* token_service_;
59 GaiaAuthFetcher fetcher_;
61 DISALLOW_COPY_AND_ASSIGN(RevokeServerRefreshToken);
64 MutableProfileOAuth2TokenService::
65 RevokeServerRefreshToken::RevokeServerRefreshToken(
66 MutableProfileOAuth2TokenService* token_service,
67 const std::string& refresh_token)
68 : token_service_(token_service),
69 fetcher_(this, GaiaConstants::kChromeSource,
70 token_service_->GetRequestContext()) {
71 fetcher_.StartRevokeOAuth2Token(refresh_token);
74 MutableProfileOAuth2TokenService::
75 RevokeServerRefreshToken::~RevokeServerRefreshToken() {}
77 void MutableProfileOAuth2TokenService::
78 RevokeServerRefreshToken::OnOAuth2RevokeTokenCompleted() {
79 // |this| pointer will be deleted when removed from the vector, so don't
80 // access any members after call to erase().
81 token_service_->server_revokes_.erase(
82 std::find(token_service_->server_revokes_.begin(),
83 token_service_->server_revokes_.end(),
87 MutableProfileOAuth2TokenService::AccountInfo::AccountInfo(
88 ProfileOAuth2TokenService* token_service,
89 const std::string& account_id,
90 const std::string& refresh_token)
91 : token_service_(token_service),
92 account_id_(account_id),
93 refresh_token_(refresh_token),
94 last_auth_error_(GoogleServiceAuthError::NONE) {
95 DCHECK(token_service_);
96 DCHECK(!account_id_.empty());
97 token_service_->signin_global_error()->AddProvider(this);
100 MutableProfileOAuth2TokenService::AccountInfo::~AccountInfo() {
101 token_service_->signin_global_error()->RemoveProvider(this);
104 void MutableProfileOAuth2TokenService::AccountInfo::SetLastAuthError(
105 const GoogleServiceAuthError& error) {
106 if (error.state() != last_auth_error_.state()) {
107 last_auth_error_ = error;
108 token_service_->signin_global_error()->AuthStatusChanged();
113 MutableProfileOAuth2TokenService::AccountInfo::GetAccountId() const {
117 GoogleServiceAuthError
118 MutableProfileOAuth2TokenService::AccountInfo::GetAuthStatus() const {
119 return last_auth_error_;
122 MutableProfileOAuth2TokenService::MutableProfileOAuth2TokenService()
123 : web_data_service_request_(0) {
126 MutableProfileOAuth2TokenService::~MutableProfileOAuth2TokenService() {
127 DCHECK(server_revokes_.empty());
130 void MutableProfileOAuth2TokenService::Shutdown() {
131 server_revokes_.clear();
132 CancelWebTokenFetch();
134 refresh_tokens_.clear();
136 ProfileOAuth2TokenService::Shutdown();
139 std::string MutableProfileOAuth2TokenService::GetRefreshToken(
140 const std::string& account_id) {
141 AccountInfoMap::const_iterator iter = refresh_tokens_.find(account_id);
142 if (iter != refresh_tokens_.end())
143 return iter->second->refresh_token();
144 return std::string();
147 net::URLRequestContextGetter*
148 MutableProfileOAuth2TokenService::GetRequestContext() {
149 return profile()->GetRequestContext();
152 void MutableProfileOAuth2TokenService::LoadCredentials(
153 const std::string& primary_account_id) {
154 DCHECK(!primary_account_id.empty());
155 DCHECK(loading_primary_account_id_.empty());
156 DCHECK_EQ(0, web_data_service_request_);
159 refresh_tokens().clear();
160 loading_primary_account_id_ = primary_account_id;
161 scoped_refptr<TokenWebData> token_web_data =
162 WebDataServiceFactory::GetTokenWebDataForProfile(
163 profile(), Profile::EXPLICIT_ACCESS);
164 if (token_web_data.get())
165 web_data_service_request_ = token_web_data->GetAllTokens(this);
168 void MutableProfileOAuth2TokenService::OnWebDataServiceRequestDone(
169 WebDataServiceBase::Handle handle,
170 const WDTypedResult* result) {
171 DCHECK_EQ(web_data_service_request_, handle);
172 web_data_service_request_ = 0;
175 DCHECK(result->GetType() == TOKEN_RESULT);
176 const WDResult<std::map<std::string, std::string> > * token_result =
177 static_cast<const WDResult<std::map<std::string, std::string> > * > (
179 LoadAllCredentialsIntoMemory(token_result->GetValue());
182 // Make sure that we have an entry for |loading_primary_account_id_| in the
183 // map. The entry could be missing if there is a corruption in the token DB
184 // while this profile is connected to an account.
185 DCHECK(!loading_primary_account_id_.empty());
186 if (refresh_tokens().count(loading_primary_account_id_) == 0) {
187 refresh_tokens()[loading_primary_account_id_].reset(
188 new AccountInfo(this, loading_primary_account_id_, std::string()));
191 // If we don't have a refresh token for a known account, signal an error.
192 for (AccountInfoMap::const_iterator i = refresh_tokens_.begin();
193 i != refresh_tokens_.end(); ++i) {
194 if (!RefreshTokenIsAvailable(i->first)) {
197 GoogleServiceAuthError(
198 GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
203 loading_primary_account_id_.clear();
206 void MutableProfileOAuth2TokenService::LoadAllCredentialsIntoMemory(
207 const std::map<std::string, std::string>& db_tokens) {
208 std::string old_login_token;
210 for (std::map<std::string, std::string>::const_iterator iter =
212 iter != db_tokens.end();
214 std::string prefixed_account_id = iter->first;
215 std::string refresh_token = iter->second;
217 if (IsLegacyRefreshTokenId(prefixed_account_id) && !refresh_token.empty())
218 old_login_token = refresh_token;
220 if (IsLegacyServiceId(prefixed_account_id)) {
221 scoped_refptr<TokenWebData> token_web_data =
222 WebDataServiceFactory::GetTokenWebDataForProfile(
223 profile(), Profile::EXPLICIT_ACCESS);
224 if (token_web_data.get())
225 token_web_data->RemoveTokenForService(prefixed_account_id);
227 DCHECK(!refresh_token.empty());
228 std::string account_id = RemoveAccountIdPrefix(prefixed_account_id);
229 refresh_tokens()[account_id].reset(
230 new AccountInfo(this, account_id, refresh_token));
231 FireRefreshTokenAvailable(account_id);
232 // TODO(fgorski): Notify diagnostic observers.
236 if (!old_login_token.empty()) {
237 DCHECK(!loading_primary_account_id_.empty());
238 if (refresh_tokens().count(loading_primary_account_id_) == 0)
239 UpdateCredentials(loading_primary_account_id_, old_login_token);
242 FireRefreshTokensLoaded();
245 void MutableProfileOAuth2TokenService::UpdateAuthError(
246 const std::string& account_id,
247 const GoogleServiceAuthError& error) {
248 // Do not report connection errors as these are not actually auth errors.
249 // We also want to avoid masking a "real" auth error just because we
250 // subsequently get a transient network error.
251 if (error.state() == GoogleServiceAuthError::CONNECTION_FAILED ||
252 error.state() == GoogleServiceAuthError::SERVICE_UNAVAILABLE)
256 // ProfileOauth2TokenService does not manage the refresh tokens on iOS - the
257 // account info on iOS is only used to manage the authentication error state.
258 // Simply add an account info entry with empty refresh token if none exists.
259 if (refresh_tokens_.count(account_id) == 0) {
260 refresh_tokens_[account_id].reset(
261 new AccountInfo(this, account_id, std::string()));
265 if (refresh_tokens_.count(account_id) == 0) {
266 // This could happen if the preferences have been corrupted (see
267 // http://crbug.com/321370). In a Debug build that would be a bug, but in a
268 // Release build we want to deal with it gracefully.
272 refresh_tokens_[account_id]->SetLastAuthError(error);
275 std::vector<std::string> MutableProfileOAuth2TokenService::GetAccounts() {
276 std::vector<std::string> account_ids;
277 for (AccountInfoMap::const_iterator iter = refresh_tokens_.begin();
278 iter != refresh_tokens_.end(); ++iter) {
279 account_ids.push_back(iter->first);
284 void MutableProfileOAuth2TokenService::UpdateCredentials(
285 const std::string& account_id,
286 const std::string& refresh_token) {
287 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
288 DCHECK(!account_id.empty());
289 DCHECK(!refresh_token.empty());
291 bool refresh_token_present = refresh_tokens_.count(account_id) > 0;
292 if (!refresh_token_present ||
293 refresh_tokens_[account_id]->refresh_token() != refresh_token) {
294 // If token present, and different from the new one, cancel its requests,
295 // and clear the entries in cache related to that account.
296 if (refresh_token_present) {
297 RevokeCredentialsOnServer(refresh_tokens_[account_id]->refresh_token());
298 CancelRequestsForAccount(account_id);
299 ClearCacheForAccount(account_id);
300 refresh_tokens_[account_id]->set_refresh_token(refresh_token);
302 refresh_tokens_[account_id].reset(
303 new AccountInfo(this, account_id, refresh_token));
306 // Save the token in memory and in persistent store.
307 PersistCredentials(account_id, refresh_token);
309 UpdateAuthError(account_id, GoogleServiceAuthError::AuthErrorNone());
310 FireRefreshTokenAvailable(account_id);
314 void MutableProfileOAuth2TokenService::RevokeCredentials(
315 const std::string& account_id) {
316 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
318 if (refresh_tokens_.count(account_id) > 0) {
319 RevokeCredentialsOnServer(refresh_tokens_[account_id]->refresh_token());
320 CancelRequestsForAccount(account_id);
321 ClearCacheForAccount(account_id);
322 refresh_tokens_.erase(account_id);
323 ClearPersistedCredentials(account_id);
324 FireRefreshTokenRevoked(account_id);
328 void MutableProfileOAuth2TokenService::PersistCredentials(
329 const std::string& account_id,
330 const std::string& refresh_token) {
331 scoped_refptr<TokenWebData> token_web_data =
332 WebDataServiceFactory::GetTokenWebDataForProfile(
333 profile(), Profile::EXPLICIT_ACCESS);
334 if (token_web_data.get()) {
335 token_web_data->SetTokenForService(ApplyAccountIdPrefix(account_id),
340 void MutableProfileOAuth2TokenService::ClearPersistedCredentials(
341 const std::string& account_id) {
342 scoped_refptr<TokenWebData> token_web_data =
343 WebDataServiceFactory::GetTokenWebDataForProfile(
344 profile(), Profile::EXPLICIT_ACCESS);
345 if (token_web_data.get())
346 token_web_data->RemoveTokenForService(ApplyAccountIdPrefix(account_id));
349 void MutableProfileOAuth2TokenService::RevokeAllCredentials() {
350 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
351 CancelWebTokenFetch();
354 AccountInfoMap tokens = refresh_tokens_;
355 for (AccountInfoMap::iterator i = tokens.begin(); i != tokens.end(); ++i)
356 RevokeCredentials(i->first);
358 DCHECK_EQ(0u, refresh_tokens_.size());
361 void MutableProfileOAuth2TokenService::RevokeCredentialsOnServer(
362 const std::string& refresh_token) {
363 // Keep track or all server revoke requests. This way they can be deleted
364 // before the token service is shutdown and won't outlive the profile.
365 server_revokes_.push_back(
366 new RevokeServerRefreshToken(this, refresh_token));
369 void MutableProfileOAuth2TokenService::CancelWebTokenFetch() {
370 if (web_data_service_request_ != 0) {
371 scoped_refptr<TokenWebData> token_web_data =
372 WebDataServiceFactory::GetTokenWebDataForProfile(
373 profile(), Profile::EXPLICIT_ACCESS);
374 DCHECK(token_web_data.get());
375 token_web_data->CancelRequest(web_data_service_request_);
376 web_data_service_request_ = 0;