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.
4
5 #include "chrome/browser/signin/profile_oauth2_token_service.h"
6
7 #include "base/bind.h"
8 #include "base/message_loop/message_loop.h"
9 #include "base/stl_util.h"
10 #include "base/time/time.h"
11 #include "chrome/browser/chrome_notification_types.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/browser/signin/signin_global_error.h"
14 #include "chrome/browser/signin/signin_manager.h"
15 #include "chrome/browser/signin/signin_manager_factory.h"
16 #include "chrome/browser/ui/global_error/global_error_service.h"
17 #include "chrome/browser/ui/global_error/global_error_service_factory.h"
18 #include "chrome/browser/webdata/token_web_data.h"
19 #include "content/public/browser/browser_thread.h"
20 #include "content/public/browser/notification_details.h"
21 #include "content/public/browser/notification_source.h"
22 #include "google_apis/gaia/gaia_auth_fetcher.h"
23 #include "google_apis/gaia/gaia_constants.h"
24 #include "google_apis/gaia/google_service_auth_error.h"
25 #include "net/url_request/url_request_context_getter.h"
26
27 namespace {
28
29 // |kAccountIdPrefix| is in the process in being moved to
30 // mutable_profile_oauth2_token_service.cc. It is duplicated here for a short
31 // period.
32 const char kAccountIdPrefix[] = "AccountId-";
ApplyAccountIdPrefix(const std::string & account_id)33 std::string ApplyAccountIdPrefix(const std::string& account_id) {
34 return kAccountIdPrefix + account_id;
35 }
36
37 // This class sends a request to GAIA to revoke the given refresh token from
38 // the server. This is a best effort attempt only. This class deletes itself
39 // when done sucessfully or otherwise.
40 class RevokeServerRefreshToken : public GaiaAuthConsumer {
41 public:
42 RevokeServerRefreshToken(const std::string& account_id,
43 net::URLRequestContextGetter* request_context);
44 virtual ~RevokeServerRefreshToken();
45
46 private:
47 // GaiaAuthConsumer overrides:
48 virtual void OnOAuth2RevokeTokenCompleted() OVERRIDE;
49
50 scoped_refptr<net::URLRequestContextGetter> request_context_;
51 scoped_ptr<GaiaAuthFetcher> fetcher_;
52
53 DISALLOW_COPY_AND_ASSIGN(RevokeServerRefreshToken);
54 };
55
RevokeServerRefreshToken(const std::string & refresh_token,net::URLRequestContextGetter * request_context)56 RevokeServerRefreshToken::RevokeServerRefreshToken(
57 const std::string& refresh_token,
58 net::URLRequestContextGetter* request_context)
59 : request_context_(request_context) {
60 fetcher_.reset(
61 new GaiaAuthFetcher(this,
62 GaiaConstants::kChromeSource,
63 request_context_.get()));
64 fetcher_->StartRevokeOAuth2Token(refresh_token);
65 }
66
~RevokeServerRefreshToken()67 RevokeServerRefreshToken::~RevokeServerRefreshToken() {}
68
OnOAuth2RevokeTokenCompleted()69 void RevokeServerRefreshToken::OnOAuth2RevokeTokenCompleted() {
70 delete this;
71 }
72
73 } // namespace
74
75
AccountInfo(ProfileOAuth2TokenService * token_service,const std::string & account_id,const std::string & refresh_token)76 ProfileOAuth2TokenService::AccountInfo::AccountInfo(
77 ProfileOAuth2TokenService* token_service,
78 const std::string& account_id,
79 const std::string& refresh_token)
80 : token_service_(token_service),
81 account_id_(account_id),
82 refresh_token_(refresh_token),
83 last_auth_error_(GoogleServiceAuthError::NONE) {
84 DCHECK(token_service_);
85 DCHECK(!account_id_.empty());
86 token_service_->signin_global_error()->AddProvider(this);
87 }
88
~AccountInfo()89 ProfileOAuth2TokenService::AccountInfo::~AccountInfo() {
90 token_service_->signin_global_error()->RemoveProvider(this);
91 }
92
SetLastAuthError(const GoogleServiceAuthError & error)93 void ProfileOAuth2TokenService::AccountInfo::SetLastAuthError(
94 const GoogleServiceAuthError& error) {
95 if (error.state() != last_auth_error_.state()) {
96 last_auth_error_ = error;
97 token_service_->signin_global_error()->AuthStatusChanged();
98 }
99 }
100
GetAccountId() const101 std::string ProfileOAuth2TokenService::AccountInfo::GetAccountId() const {
102 return account_id_;
103 }
104
105 GoogleServiceAuthError
GetAuthStatus() const106 ProfileOAuth2TokenService::AccountInfo::GetAuthStatus() const {
107 return last_auth_error_;
108 }
109
ProfileOAuth2TokenService()110 ProfileOAuth2TokenService::ProfileOAuth2TokenService()
111 : profile_(NULL) {
112 }
113
~ProfileOAuth2TokenService()114 ProfileOAuth2TokenService::~ProfileOAuth2TokenService() {
115 DCHECK(!signin_global_error_.get()) <<
116 "ProfileOAuth2TokenService::Initialize called but not "
117 "ProfileOAuth2TokenService::Shutdown";
118 }
119
Initialize(Profile * profile)120 void ProfileOAuth2TokenService::Initialize(Profile* profile) {
121 DCHECK(profile);
122 DCHECK(!profile_);
123 profile_ = profile;
124
125 signin_global_error_.reset(new SigninGlobalError(profile));
126 GlobalErrorServiceFactory::GetForProfile(profile_)->AddGlobalError(
127 signin_global_error_.get());
128 }
129
Shutdown()130 void ProfileOAuth2TokenService::Shutdown() {
131 DCHECK(profile_) << "Shutdown() called without matching call to Initialize()";
132 CancelAllRequests();
133 refresh_tokens_.clear();
134 GlobalErrorServiceFactory::GetForProfile(profile_)->RemoveGlobalError(
135 signin_global_error_.get());
136 signin_global_error_.reset();
137 }
138
GetRefreshToken(const std::string & account_id)139 std::string ProfileOAuth2TokenService::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();
145 }
146
GetRequestContext()147 net::URLRequestContextGetter* ProfileOAuth2TokenService::GetRequestContext() {
148 return profile_->GetRequestContext();
149 }
150
UpdateAuthError(const std::string & account_id,const GoogleServiceAuthError & error)151 void ProfileOAuth2TokenService::UpdateAuthError(
152 const std::string& account_id,
153 const GoogleServiceAuthError& error) {
154 // Do not report connection errors as these are not actually auth errors.
155 // We also want to avoid masking a "real" auth error just because we
156 // subsequently get a transient network error.
157 if (error.state() == GoogleServiceAuthError::CONNECTION_FAILED ||
158 error.state() == GoogleServiceAuthError::SERVICE_UNAVAILABLE)
159 return;
160
161 #if defined(OS_IOS)
162 // ProfileOauth2TokenService does not manage the refresh tokens on iOS - the
163 // account info on iOS is only used to manage the authentication error state.
164 // Simply add an account info entry with empty refresh token if none exists.
165 if (refresh_tokens_.count(account_id) == 0) {
166 refresh_tokens_[account_id].reset(
167 new AccountInfo(this, account_id, std::string()));
168 }
169 #endif
170
171 DCHECK_GT(refresh_tokens_.count(account_id), 0u);
172 refresh_tokens_[account_id]->SetLastAuthError(error);
173 }
174
GetPrimaryAccountId()175 std::string ProfileOAuth2TokenService::GetPrimaryAccountId() {
176 SigninManagerBase* signin_manager =
177 SigninManagerFactory::GetForProfileIfExists(profile_);
178 // TODO(fgorski): DCHECK(signin_manager) here - it may require update to test
179 // code and the line above (SigninManager might not exist yet).
180 return signin_manager ? signin_manager->GetAuthenticatedUsername()
181 : std::string();
182 }
183
GetAccounts()184 std::vector<std::string> ProfileOAuth2TokenService::GetAccounts() {
185 std::vector<std::string> account_ids;
186 for (AccountInfoMap::const_iterator iter = refresh_tokens_.begin();
187 iter != refresh_tokens_.end(); ++iter) {
188 account_ids.push_back(iter->first);
189 }
190 return account_ids;
191 }
192
UpdateCredentials(const std::string & account_id,const std::string & refresh_token)193 void ProfileOAuth2TokenService::UpdateCredentials(
194 const std::string& account_id,
195 const std::string& refresh_token) {
196 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
197 DCHECK(!account_id.empty());
198 DCHECK(!refresh_token.empty());
199
200 bool refresh_token_present = refresh_tokens_.count(account_id) > 0;
201 if (!refresh_token_present ||
202 refresh_tokens_[account_id]->refresh_token() != refresh_token) {
203 // If token present, and different from the new one, cancel its requests,
204 // and clear the entries in cache related to that account.
205 if (refresh_token_present) {
206 RevokeCredentialsOnServer(refresh_tokens_[account_id]->refresh_token());
207 CancelRequestsForAccount(account_id);
208 ClearCacheForAccount(account_id);
209 refresh_tokens_[account_id]->set_refresh_token(refresh_token);
210 } else {
211 refresh_tokens_[account_id].reset(
212 new AccountInfo(this, account_id, refresh_token));
213 }
214
215 // Save the token in memory and in persistent store.
216 PersistCredentials(account_id, refresh_token);
217
218 UpdateAuthError(account_id, GoogleServiceAuthError::AuthErrorNone());
219 FireRefreshTokenAvailable(account_id);
220 // TODO(fgorski): Notify diagnostic observers.
221 }
222 }
223
RevokeCredentials(const std::string & account_id)224 void ProfileOAuth2TokenService::RevokeCredentials(
225 const std::string& account_id) {
226 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
227
228 if (refresh_tokens_.count(account_id) > 0) {
229 RevokeCredentialsOnServer(refresh_tokens_[account_id]->refresh_token());
230 CancelRequestsForAccount(account_id);
231 ClearCacheForAccount(account_id);
232 refresh_tokens_.erase(account_id);
233 ClearPersistedCredentials(account_id);
234 FireRefreshTokenRevoked(account_id);
235
236 // TODO(fgorski): Notify diagnostic observers.
237 }
238 }
239
PersistCredentials(const std::string & account_id,const std::string & refresh_token)240 void ProfileOAuth2TokenService::PersistCredentials(
241 const std::string& account_id,
242 const std::string& refresh_token) {
243 scoped_refptr<TokenWebData> token_web_data =
244 TokenWebData::FromBrowserContext(profile_);
245 if (token_web_data.get()) {
246 token_web_data->SetTokenForService(ApplyAccountIdPrefix(account_id),
247 refresh_token);
248 }
249 }
250
ClearPersistedCredentials(const std::string & account_id)251 void ProfileOAuth2TokenService::ClearPersistedCredentials(
252 const std::string& account_id) {
253 scoped_refptr<TokenWebData> token_web_data =
254 TokenWebData::FromBrowserContext(profile_);
255 if (token_web_data.get())
256 token_web_data->RemoveTokenForService(ApplyAccountIdPrefix(account_id));
257 }
258
RevokeAllCredentials()259 void ProfileOAuth2TokenService::RevokeAllCredentials() {
260 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
261
262 CancelAllRequests();
263 ClearCache();
264 AccountInfoMap tokens = refresh_tokens_;
265 for (AccountInfoMap::iterator i = tokens.begin(); i != tokens.end(); ++i)
266 RevokeCredentials(i->first);
267
268 DCHECK_EQ(0u, refresh_tokens_.size());
269
270 // TODO(fgorski): Notify diagnostic observers.
271 }
272
LoadCredentials()273 void ProfileOAuth2TokenService::LoadCredentials() {
274 // Empty implementation by default.
275 }
276
RevokeCredentialsOnServer(const std::string & refresh_token)277 void ProfileOAuth2TokenService::RevokeCredentialsOnServer(
278 const std::string& refresh_token) {
279 // RevokeServerRefreshToken deletes itself when done.
280 new RevokeServerRefreshToken(refresh_token, GetRequestContext());
281 }
282