Home | History | Annotate | Download | only in signin
      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