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-"; 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 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 67 RevokeServerRefreshToken::~RevokeServerRefreshToken() {} 68 69 void RevokeServerRefreshToken::OnOAuth2RevokeTokenCompleted() { 70 delete this; 71 } 72 73 } // namespace 74 75 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 89 ProfileOAuth2TokenService::AccountInfo::~AccountInfo() { 90 token_service_->signin_global_error()->RemoveProvider(this); 91 } 92 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 101 std::string ProfileOAuth2TokenService::AccountInfo::GetAccountId() const { 102 return account_id_; 103 } 104 105 GoogleServiceAuthError 106 ProfileOAuth2TokenService::AccountInfo::GetAuthStatus() const { 107 return last_auth_error_; 108 } 109 110 ProfileOAuth2TokenService::ProfileOAuth2TokenService() 111 : profile_(NULL) { 112 } 113 114 ProfileOAuth2TokenService::~ProfileOAuth2TokenService() { 115 DCHECK(!signin_global_error_.get()) << 116 "ProfileOAuth2TokenService::Initialize called but not " 117 "ProfileOAuth2TokenService::Shutdown"; 118 } 119 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 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 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 147 net::URLRequestContextGetter* ProfileOAuth2TokenService::GetRequestContext() { 148 return profile_->GetRequestContext(); 149 } 150 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 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 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 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 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 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 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 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 273 void ProfileOAuth2TokenService::LoadCredentials() { 274 // Empty implementation by default. 275 } 276 277 void ProfileOAuth2TokenService::RevokeCredentialsOnServer( 278 const std::string& refresh_token) { 279 // RevokeServerRefreshToken deletes itself when done. 280 new RevokeServerRefreshToken(refresh_token, GetRequestContext()); 281 } 282