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