Home | History | Annotate | Download | only in host
      1 // Copyright 2014 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 "remoting/host/oauth_token_getter.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/callback.h"
      9 #include "base/strings/string_util.h"
     10 #include "google_apis/google_api_keys.h"
     11 #include "net/url_request/url_request_context_getter.h"
     12 #include "remoting/base/logging.h"
     13 
     14 namespace remoting {
     15 
     16 namespace {
     17 
     18 // Maximum number of retries on network/500 errors.
     19 const int kMaxRetries = 3;
     20 
     21 // Time when we we try to update OAuth token before its expiration.
     22 const int kTokenUpdateTimeBeforeExpirySeconds = 60;
     23 
     24 }  // namespace
     25 
     26 OAuthTokenGetter::OAuthCredentials::OAuthCredentials(
     27     const std::string& login,
     28     const std::string& refresh_token,
     29     bool is_service_account)
     30     : login(login),
     31       refresh_token(refresh_token),
     32       is_service_account(is_service_account) {
     33 }
     34 
     35 OAuthTokenGetter::OAuthTokenGetter(
     36     scoped_ptr<OAuthCredentials> oauth_credentials,
     37     scoped_refptr<net::URLRequestContextGetter> url_request_context_getter,
     38     bool auto_refresh)
     39     : oauth_credentials_(oauth_credentials.Pass()),
     40       gaia_oauth_client_(
     41           new gaia::GaiaOAuthClient(url_request_context_getter)),
     42       url_request_context_getter_(url_request_context_getter),
     43       refreshing_oauth_token_(false) {
     44   if (auto_refresh) {
     45     refresh_timer_.reset(new base::OneShotTimer<OAuthTokenGetter>());
     46   }
     47 }
     48 
     49 OAuthTokenGetter::~OAuthTokenGetter() {}
     50 
     51 void OAuthTokenGetter::OnGetTokensResponse(const std::string& user_email,
     52                                            const std::string& access_token,
     53                                            int expires_seconds) {
     54   NOTREACHED();
     55 }
     56 
     57 void OAuthTokenGetter::OnRefreshTokenResponse(
     58     const std::string& access_token,
     59     int expires_seconds) {
     60   DCHECK(CalledOnValidThread());
     61   DCHECK(oauth_credentials_.get());
     62   HOST_LOG << "Received OAuth token.";
     63 
     64   oauth_access_token_ = access_token;
     65   base::TimeDelta token_expiration =
     66       base::TimeDelta::FromSeconds(expires_seconds) -
     67       base::TimeDelta::FromSeconds(kTokenUpdateTimeBeforeExpirySeconds);
     68   auth_token_expiry_time_ = base::Time::Now() + token_expiration;
     69 
     70   if (refresh_timer_) {
     71     refresh_timer_->Stop();
     72     refresh_timer_->Start(FROM_HERE, token_expiration, this,
     73                           &OAuthTokenGetter::RefreshOAuthToken);
     74   }
     75 
     76   if (verified_email_.empty()) {
     77     gaia_oauth_client_->GetUserEmail(access_token, kMaxRetries, this);
     78   } else {
     79     refreshing_oauth_token_ = false;
     80     NotifyCallbacks(
     81         OAuthTokenGetter::SUCCESS, verified_email_, oauth_access_token_);
     82   }
     83 }
     84 
     85 void OAuthTokenGetter::OnGetUserEmailResponse(const std::string& user_email) {
     86   DCHECK(CalledOnValidThread());
     87   DCHECK(oauth_credentials_.get());
     88   HOST_LOG << "Received user info.";
     89 
     90   if (user_email != oauth_credentials_->login) {
     91     LOG(ERROR) << "OAuth token and email address do not refer to "
     92         "the same account.";
     93     OnOAuthError();
     94     return;
     95   }
     96 
     97   verified_email_ = user_email;
     98   refreshing_oauth_token_ = false;
     99 
    100   // Now that we've refreshed the token and verified that it's for the correct
    101   // user account, try to connect using the new token.
    102   NotifyCallbacks(OAuthTokenGetter::SUCCESS, user_email, oauth_access_token_);
    103 }
    104 
    105 void OAuthTokenGetter::NotifyCallbacks(Status status,
    106                      const std::string& user_email,
    107                      const std::string& access_token) {
    108   std::queue<TokenCallback> callbacks(pending_callbacks_);
    109   pending_callbacks_ = std::queue<TokenCallback>();
    110 
    111   while (!callbacks.empty()) {
    112     callbacks.front().Run(status, user_email, access_token);
    113     callbacks.pop();
    114   }
    115 }
    116 
    117 void OAuthTokenGetter::OnOAuthError() {
    118   DCHECK(CalledOnValidThread());
    119   LOG(ERROR) << "OAuth: invalid credentials.";
    120   refreshing_oauth_token_ = false;
    121 
    122   // Throw away invalid credentials and force a refresh.
    123   oauth_access_token_.clear();
    124   auth_token_expiry_time_ = base::Time();
    125   verified_email_.clear();
    126 
    127   NotifyCallbacks(OAuthTokenGetter::AUTH_ERROR, std::string(), std::string());
    128 }
    129 
    130 void OAuthTokenGetter::OnNetworkError(int response_code) {
    131   DCHECK(CalledOnValidThread());
    132   LOG(ERROR) << "Network error when trying to update OAuth token: "
    133              << response_code;
    134   refreshing_oauth_token_ = false;
    135   NotifyCallbacks(
    136       OAuthTokenGetter::NETWORK_ERROR, std::string(), std::string());
    137 }
    138 
    139 void OAuthTokenGetter::CallWithToken(const TokenCallback& on_access_token) {
    140   DCHECK(CalledOnValidThread());
    141   bool need_new_auth_token = auth_token_expiry_time_.is_null() ||
    142       base::Time::Now() >= auth_token_expiry_time_ ||
    143       verified_email_.empty();
    144 
    145   if (need_new_auth_token) {
    146     pending_callbacks_.push(on_access_token);
    147     if (!refreshing_oauth_token_)
    148       RefreshOAuthToken();
    149   } else {
    150     on_access_token.Run(
    151         SUCCESS, oauth_credentials_->login, oauth_access_token_);
    152   }
    153 }
    154 
    155 void OAuthTokenGetter::RefreshOAuthToken() {
    156   DCHECK(CalledOnValidThread());
    157   HOST_LOG << "Refreshing OAuth token.";
    158   DCHECK(!refreshing_oauth_token_);
    159 
    160   // Service accounts use different API keys, as they use the client app flow.
    161   google_apis::OAuth2Client oauth2_client =
    162       oauth_credentials_->is_service_account ?
    163       google_apis::CLIENT_REMOTING_HOST : google_apis::CLIENT_REMOTING;
    164 
    165   gaia::OAuthClientInfo client_info = {
    166     google_apis::GetOAuth2ClientID(oauth2_client),
    167     google_apis::GetOAuth2ClientSecret(oauth2_client),
    168     // Redirect URL is only used when getting tokens from auth code. It
    169     // is not required when getting access tokens.
    170     ""
    171   };
    172 
    173   refreshing_oauth_token_ = true;
    174   std::vector<std::string> empty_scope_list;  // Use scope from refresh token.
    175   gaia_oauth_client_->RefreshToken(
    176       client_info, oauth_credentials_->refresh_token, empty_scope_list,
    177       kMaxRetries, this);
    178 }
    179 
    180 }  // namespace remoting
    181