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
);
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 RevokeServerRefreshToken
: public GaiaAuthConsumer
{
47 RevokeServerRefreshToken(const std::string
& account_id
,
48 net::URLRequestContextGetter
* request_context
);
49 virtual ~RevokeServerRefreshToken();
52 // GaiaAuthConsumer overrides:
53 virtual void OnOAuth2RevokeTokenCompleted() OVERRIDE
;
55 scoped_refptr
<net::URLRequestContextGetter
> request_context_
;
56 GaiaAuthFetcher fetcher_
;
58 DISALLOW_COPY_AND_ASSIGN(RevokeServerRefreshToken
);
61 RevokeServerRefreshToken::RevokeServerRefreshToken(
62 const std::string
& refresh_token
,
63 net::URLRequestContextGetter
* request_context
)
64 : request_context_(request_context
),
65 fetcher_(this, GaiaConstants::kChromeSource
, request_context
) {
66 fetcher_
.StartRevokeOAuth2Token(refresh_token
);
69 RevokeServerRefreshToken::~RevokeServerRefreshToken() {}
71 void RevokeServerRefreshToken::OnOAuth2RevokeTokenCompleted() {
77 MutableProfileOAuth2TokenService::AccountInfo::AccountInfo(
78 ProfileOAuth2TokenService
* token_service
,
79 const std::string
& account_id
,
80 const std::string
& refresh_token
)
81 : token_service_(token_service
),
82 account_id_(account_id
),
83 refresh_token_(refresh_token
),
84 last_auth_error_(GoogleServiceAuthError::NONE
) {
85 DCHECK(token_service_
);
86 DCHECK(!account_id_
.empty());
87 token_service_
->signin_global_error()->AddProvider(this);
90 MutableProfileOAuth2TokenService::AccountInfo::~AccountInfo() {
91 token_service_
->signin_global_error()->RemoveProvider(this);
94 void MutableProfileOAuth2TokenService::AccountInfo::SetLastAuthError(
95 const GoogleServiceAuthError
& error
) {
96 if (error
.state() != last_auth_error_
.state()) {
97 last_auth_error_
= error
;
98 token_service_
->signin_global_error()->AuthStatusChanged();
103 MutableProfileOAuth2TokenService::AccountInfo::GetAccountId() const {
107 GoogleServiceAuthError
108 MutableProfileOAuth2TokenService::AccountInfo::GetAuthStatus() const {
109 return last_auth_error_
;
112 MutableProfileOAuth2TokenService::MutableProfileOAuth2TokenService()
113 : web_data_service_request_(0) {
116 MutableProfileOAuth2TokenService::~MutableProfileOAuth2TokenService() {
119 void MutableProfileOAuth2TokenService::Shutdown() {
120 if (web_data_service_request_
!= 0) {
121 scoped_refptr
<TokenWebData
> token_web_data
=
122 WebDataServiceFactory::GetTokenWebDataForProfile(
123 profile(), Profile::EXPLICIT_ACCESS
);
124 DCHECK(token_web_data
.get());
125 token_web_data
->CancelRequest(web_data_service_request_
);
126 web_data_service_request_
= 0;
130 refresh_tokens_
.clear();
132 ProfileOAuth2TokenService::Shutdown();
135 std::string
MutableProfileOAuth2TokenService::GetRefreshToken(
136 const std::string
& account_id
) {
137 AccountInfoMap::const_iterator iter
= refresh_tokens_
.find(account_id
);
138 if (iter
!= refresh_tokens_
.end())
139 return iter
->second
->refresh_token();
140 return std::string();
143 net::URLRequestContextGetter
*
144 MutableProfileOAuth2TokenService::GetRequestContext() {
145 return profile()->GetRequestContext();
148 void MutableProfileOAuth2TokenService::LoadCredentials() {
149 DCHECK_EQ(0, web_data_service_request_
);
152 refresh_tokens().clear();
153 scoped_refptr
<TokenWebData
> token_web_data
=
154 WebDataServiceFactory::GetTokenWebDataForProfile(
155 profile(), Profile::EXPLICIT_ACCESS
);
156 if (token_web_data
.get())
157 web_data_service_request_
= token_web_data
->GetAllTokens(this);
160 void MutableProfileOAuth2TokenService::OnWebDataServiceRequestDone(
161 WebDataServiceBase::Handle handle
,
162 const WDTypedResult
* result
) {
163 DCHECK_EQ(web_data_service_request_
, handle
);
164 web_data_service_request_
= 0;
167 DCHECK(result
->GetType() == TOKEN_RESULT
);
168 const WDResult
<std::map
<std::string
, std::string
> > * token_result
=
169 static_cast<const WDResult
<std::map
<std::string
, std::string
> > * > (
171 LoadAllCredentialsIntoMemory(token_result
->GetValue());
174 std::string account_id
= GetPrimaryAccountId();
176 // If |account_id| is not empty, make sure that we have an entry in the
177 // map for it. The entry could be missing if there is a corruption in
178 // the token DB while this profile is connected to an account.
179 if (!account_id
.empty() && refresh_tokens().count(account_id
) == 0) {
180 refresh_tokens()[account_id
].reset(
181 new AccountInfo(this, account_id
, std::string()));
184 // If we don't have a refresh token for the primary account, signal a signin
186 if (!account_id
.empty() && !RefreshTokenIsAvailable(account_id
)) {
189 GoogleServiceAuthError(
190 GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS
));
194 void MutableProfileOAuth2TokenService::LoadAllCredentialsIntoMemory(
195 const std::map
<std::string
, std::string
>& db_tokens
) {
196 std::string old_login_token
;
198 for (std::map
<std::string
, std::string
>::const_iterator iter
=
200 iter
!= db_tokens
.end();
202 std::string prefixed_account_id
= iter
->first
;
203 std::string refresh_token
= iter
->second
;
205 if (IsLegacyRefreshTokenId(prefixed_account_id
) && !refresh_token
.empty())
206 old_login_token
= refresh_token
;
208 if (IsLegacyServiceId(prefixed_account_id
)) {
209 scoped_refptr
<TokenWebData
> token_web_data
=
210 WebDataServiceFactory::GetTokenWebDataForProfile(
211 profile(), Profile::EXPLICIT_ACCESS
);
212 if (token_web_data
.get())
213 token_web_data
->RemoveTokenForService(prefixed_account_id
);
215 DCHECK(!refresh_token
.empty());
216 std::string account_id
= RemoveAccountIdPrefix(prefixed_account_id
);
217 refresh_tokens()[account_id
].reset(
218 new AccountInfo(this, account_id
, refresh_token
));
219 FireRefreshTokenAvailable(account_id
);
220 // TODO(fgorski): Notify diagnostic observers.
224 if (!old_login_token
.empty()) {
225 std::string account_id
= GetAccountIdForMigratingRefreshToken();
227 if (refresh_tokens().count(account_id
) == 0)
228 UpdateCredentials(account_id
, old_login_token
);
231 FireRefreshTokensLoaded();
234 void MutableProfileOAuth2TokenService::UpdateAuthError(
235 const std::string
& account_id
,
236 const GoogleServiceAuthError
& error
) {
237 // Do not report connection errors as these are not actually auth errors.
238 // We also want to avoid masking a "real" auth error just because we
239 // subsequently get a transient network error.
240 if (error
.state() == GoogleServiceAuthError::CONNECTION_FAILED
||
241 error
.state() == GoogleServiceAuthError::SERVICE_UNAVAILABLE
)
245 // ProfileOauth2TokenService does not manage the refresh tokens on iOS - the
246 // account info on iOS is only used to manage the authentication error state.
247 // Simply add an account info entry with empty refresh token if none exists.
248 if (refresh_tokens_
.count(account_id
) == 0) {
249 refresh_tokens_
[account_id
].reset(
250 new AccountInfo(this, account_id
, std::string()));
254 if (refresh_tokens_
.count(account_id
) == 0) {
255 // This could happen if the preferences have been corrupted (see
256 // http://crbug.com/321370). In a Debug build that would be a bug, but in a
257 // Release build we want to deal with it gracefully.
261 refresh_tokens_
[account_id
]->SetLastAuthError(error
);
264 std::vector
<std::string
> MutableProfileOAuth2TokenService::GetAccounts() {
265 std::vector
<std::string
> account_ids
;
266 for (AccountInfoMap::const_iterator iter
= refresh_tokens_
.begin();
267 iter
!= refresh_tokens_
.end(); ++iter
) {
268 account_ids
.push_back(iter
->first
);
273 void MutableProfileOAuth2TokenService::UpdateCredentials(
274 const std::string
& account_id
,
275 const std::string
& refresh_token
) {
276 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
277 DCHECK(!account_id
.empty());
278 DCHECK(!refresh_token
.empty());
280 bool refresh_token_present
= refresh_tokens_
.count(account_id
) > 0;
281 if (!refresh_token_present
||
282 refresh_tokens_
[account_id
]->refresh_token() != refresh_token
) {
283 // If token present, and different from the new one, cancel its requests,
284 // and clear the entries in cache related to that account.
285 if (refresh_token_present
) {
286 RevokeCredentialsOnServer(refresh_tokens_
[account_id
]->refresh_token());
287 CancelRequestsForAccount(account_id
);
288 ClearCacheForAccount(account_id
);
289 refresh_tokens_
[account_id
]->set_refresh_token(refresh_token
);
291 refresh_tokens_
[account_id
].reset(
292 new AccountInfo(this, account_id
, refresh_token
));
295 // Save the token in memory and in persistent store.
296 PersistCredentials(account_id
, refresh_token
);
298 UpdateAuthError(account_id
, GoogleServiceAuthError::AuthErrorNone());
299 FireRefreshTokenAvailable(account_id
);
300 // TODO(fgorski): Notify diagnostic observers.
304 void MutableProfileOAuth2TokenService::RevokeCredentials(
305 const std::string
& account_id
) {
306 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
308 if (refresh_tokens_
.count(account_id
) > 0) {
309 RevokeCredentialsOnServer(refresh_tokens_
[account_id
]->refresh_token());
310 CancelRequestsForAccount(account_id
);
311 ClearCacheForAccount(account_id
);
312 refresh_tokens_
.erase(account_id
);
313 ClearPersistedCredentials(account_id
);
314 FireRefreshTokenRevoked(account_id
);
316 // TODO(fgorski): Notify diagnostic observers.
320 void MutableProfileOAuth2TokenService::PersistCredentials(
321 const std::string
& account_id
,
322 const std::string
& refresh_token
) {
323 scoped_refptr
<TokenWebData
> token_web_data
=
324 WebDataServiceFactory::GetTokenWebDataForProfile(
325 profile(), Profile::EXPLICIT_ACCESS
);
326 if (token_web_data
.get()) {
327 token_web_data
->SetTokenForService(ApplyAccountIdPrefix(account_id
),
332 void MutableProfileOAuth2TokenService::ClearPersistedCredentials(
333 const std::string
& account_id
) {
334 scoped_refptr
<TokenWebData
> token_web_data
=
335 WebDataServiceFactory::GetTokenWebDataForProfile(
336 profile(), Profile::EXPLICIT_ACCESS
);
337 if (token_web_data
.get())
338 token_web_data
->RemoveTokenForService(ApplyAccountIdPrefix(account_id
));
341 void MutableProfileOAuth2TokenService::RevokeAllCredentials() {
342 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
346 AccountInfoMap tokens
= refresh_tokens_
;
347 for (AccountInfoMap::iterator i
= tokens
.begin(); i
!= tokens
.end(); ++i
)
348 RevokeCredentials(i
->first
);
350 DCHECK_EQ(0u, refresh_tokens_
.size());
352 // TODO(fgorski): Notify diagnostic observers.
356 MutableProfileOAuth2TokenService::GetAccountIdForMigratingRefreshToken() {
357 #if defined(ENABLE_MANAGED_USERS)
358 // TODO(bauerb): Make sure that only services that can deal with supervised
359 // users see the supervised user token.
360 if (profile()->IsManaged())
361 return managed_users::kManagedUserPseudoEmail
;
364 return GetPrimaryAccountId();
367 void MutableProfileOAuth2TokenService::RevokeCredentialsOnServer(
368 const std::string
& refresh_token
) {
369 // RevokeServerRefreshToken deletes itself when done.
370 new RevokeServerRefreshToken(refresh_token
, GetRequestContext());