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