• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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