15d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved. 25d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// found in the LICENSE file. 45d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 55d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "remoting/host/oauth_token_getter.h" 65d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 75d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/bind.h" 85d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/callback.h" 95d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/strings/string_util.h" 105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "google_apis/google_api_keys.h" 115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "net/url_request/url_request_context_getter.h" 125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "remoting/base/logging.h" 135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)namespace remoting { 155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)namespace { 175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Maximum number of retries on network/500 errors. 195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)const int kMaxRetries = 3; 205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Time when we we try to update OAuth token before its expiration. 225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)const int kTokenUpdateTimeBeforeExpirySeconds = 60; 235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} // namespace 255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)OAuthTokenGetter::OAuthCredentials::OAuthCredentials( 275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const std::string& login, 285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const std::string& refresh_token, 295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) bool is_service_account) 305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) : login(login), 315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) refresh_token(refresh_token), 325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) is_service_account(is_service_account) { 335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)OAuthTokenGetter::OAuthTokenGetter( 365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) scoped_ptr<OAuthCredentials> oauth_credentials, 37cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) scoped_refptr<net::URLRequestContextGetter> url_request_context_getter, 38cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) bool auto_refresh) 395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) : oauth_credentials_(oauth_credentials.Pass()), 405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gaia_oauth_client_( 411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci new gaia::GaiaOAuthClient(url_request_context_getter.get())), 425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) url_request_context_getter_(url_request_context_getter), 435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) refreshing_oauth_token_(false) { 44cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (auto_refresh) { 45cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) refresh_timer_.reset(new base::OneShotTimer<OAuthTokenGetter>()); 46cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)OAuthTokenGetter::~OAuthTokenGetter() {} 505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void OAuthTokenGetter::OnGetTokensResponse(const std::string& user_email, 525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const std::string& access_token, 535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) int expires_seconds) { 545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) NOTREACHED(); 555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void OAuthTokenGetter::OnRefreshTokenResponse( 585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const std::string& access_token, 595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) int expires_seconds) { 605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) DCHECK(CalledOnValidThread()); 615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) DCHECK(oauth_credentials_.get()); 625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) HOST_LOG << "Received OAuth token."; 635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) oauth_access_token_ = access_token; 65cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) base::TimeDelta token_expiration = 665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) base::TimeDelta::FromSeconds(expires_seconds) - 675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) base::TimeDelta::FromSeconds(kTokenUpdateTimeBeforeExpirySeconds); 68cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) auth_token_expiry_time_ = base::Time::Now() + token_expiration; 69cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 70cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (refresh_timer_) { 71cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) refresh_timer_->Stop(); 72cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) refresh_timer_->Start(FROM_HERE, token_expiration, this, 73cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) &OAuthTokenGetter::RefreshOAuthToken); 74cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 76cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (verified_email_.empty()) { 77cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) gaia_oauth_client_->GetUserEmail(access_token, kMaxRetries, this); 78cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else { 79cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) refreshing_oauth_token_ = false; 80cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) NotifyCallbacks( 81cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) OAuthTokenGetter::SUCCESS, verified_email_, oauth_access_token_); 82cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void OAuthTokenGetter::OnGetUserEmailResponse(const std::string& user_email) { 865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) DCHECK(CalledOnValidThread()); 875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) DCHECK(oauth_credentials_.get()); 885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) HOST_LOG << "Received user info."; 895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (user_email != oauth_credentials_->login) { 915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) LOG(ERROR) << "OAuth token and email address do not refer to " 925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) "the same account."; 935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) OnOAuthError(); 945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return; 955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 97cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) verified_email_ = user_email; 985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) refreshing_oauth_token_ = false; 995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Now that we've refreshed the token and verified that it's for the correct 1015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // user account, try to connect using the new token. 1025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) NotifyCallbacks(OAuthTokenGetter::SUCCESS, user_email, oauth_access_token_); 1035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 1045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void OAuthTokenGetter::NotifyCallbacks(Status status, 1065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const std::string& user_email, 1075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const std::string& access_token) { 1085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) std::queue<TokenCallback> callbacks(pending_callbacks_); 1095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) pending_callbacks_ = std::queue<TokenCallback>(); 1105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) while (!callbacks.empty()) { 1125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) callbacks.front().Run(status, user_email, access_token); 1135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) callbacks.pop(); 1145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 1155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 1165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void OAuthTokenGetter::OnOAuthError() { 1185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) DCHECK(CalledOnValidThread()); 1195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) LOG(ERROR) << "OAuth: invalid credentials."; 1205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) refreshing_oauth_token_ = false; 121cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 122cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Throw away invalid credentials and force a refresh. 123cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) oauth_access_token_.clear(); 124cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) auth_token_expiry_time_ = base::Time(); 125cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) verified_email_.clear(); 126cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 1275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) NotifyCallbacks(OAuthTokenGetter::AUTH_ERROR, std::string(), std::string()); 1285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 1295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void OAuthTokenGetter::OnNetworkError(int response_code) { 1315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) DCHECK(CalledOnValidThread()); 1325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) LOG(ERROR) << "Network error when trying to update OAuth token: " 1335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) << response_code; 1345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) refreshing_oauth_token_ = false; 1355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) NotifyCallbacks( 1365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) OAuthTokenGetter::NETWORK_ERROR, std::string(), std::string()); 1375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 1385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void OAuthTokenGetter::CallWithToken(const TokenCallback& on_access_token) { 1405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) DCHECK(CalledOnValidThread()); 141cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) bool need_new_auth_token = auth_token_expiry_time_.is_null() || 142cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) base::Time::Now() >= auth_token_expiry_time_ || 143cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) verified_email_.empty(); 144cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 1455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (need_new_auth_token) { 1465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) pending_callbacks_.push(on_access_token); 1475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (!refreshing_oauth_token_) 1485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) RefreshOAuthToken(); 1495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } else { 1505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) on_access_token.Run( 1515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) SUCCESS, oauth_credentials_->login, oauth_access_token_); 1525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 1535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 1545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void OAuthTokenGetter::RefreshOAuthToken() { 1565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) DCHECK(CalledOnValidThread()); 1575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) HOST_LOG << "Refreshing OAuth token."; 1585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) DCHECK(!refreshing_oauth_token_); 1595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Service accounts use different API keys, as they use the client app flow. 1615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) google_apis::OAuth2Client oauth2_client = 1625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) oauth_credentials_->is_service_account ? 1635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) google_apis::CLIENT_REMOTING_HOST : google_apis::CLIENT_REMOTING; 1645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gaia::OAuthClientInfo client_info = { 1665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) google_apis::GetOAuth2ClientID(oauth2_client), 1675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) google_apis::GetOAuth2ClientSecret(oauth2_client), 1685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Redirect URL is only used when getting tokens from auth code. It 1695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // is not required when getting access tokens. 1705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) "" 1715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) }; 1725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) refreshing_oauth_token_ = true; 1745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) std::vector<std::string> empty_scope_list; // Use scope from refresh token. 1755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gaia_oauth_client_->RefreshToken( 1765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) client_info, oauth_credentials_->refresh_token, empty_scope_list, 1775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) kMaxRetries, this); 1785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 1795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} // namespace remoting 181