diff options
author | jamiewalch@chromium.org <jamiewalch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-03-20 20:08:40 +0000 |
---|---|---|
committer | jamiewalch@chromium.org <jamiewalch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-03-20 20:08:40 +0000 |
commit | b8f85978f9e45f66eec6b8581b8ae54f46014da0 (patch) | |
tree | 5fd56df69a5255519e591e2367b89d0dd3f91921 /remoting | |
parent | 05454a435deac9bce39960ea21e218ebde7d17b5 (diff) | |
download | chromium_src-b8f85978f9e45f66eec6b8581b8ae54f46014da0.zip chromium_src-b8f85978f9e45f66eec6b8581b8ae54f46014da0.tar.gz chromium_src-b8f85978f9e45f66eec6b8581b8ae54f46014da0.tar.bz2 |
Implemented OAuth2 support for remoting_me2me_host.
Note that this still doesn't allow hosts to be registered using OAuth, as that's
still the domain of the Python script.
BUG=None
TEST=Modify auth.json so that the xmpp_auth_token is your OAuth refresh token (get it from the web-apps local storage) and add the following line: "xmpp_auth_service": "oauth-refresh"
Review URL: http://codereview.chromium.org/9721008
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@127758 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting')
-rw-r--r-- | remoting/host/host_config.cc | 1 | ||||
-rw-r--r-- | remoting/host/host_config.h | 2 | ||||
-rw-r--r-- | remoting/host/oauth_client.cc | 52 | ||||
-rw-r--r-- | remoting/host/oauth_client.h | 44 | ||||
-rw-r--r-- | remoting/host/remoting_me2me_host.cc | 73 |
5 files changed, 148 insertions, 24 deletions
diff --git a/remoting/host/host_config.cc b/remoting/host/host_config.cc index 8841b8b..96053e6 100644 --- a/remoting/host/host_config.cc +++ b/remoting/host/host_config.cc @@ -9,6 +9,7 @@ namespace remoting { const char kHostEnabledConfigPath[] = "enabled"; const char kXmppLoginConfigPath[] = "xmpp_login"; const char kXmppAuthTokenConfigPath[] = "xmpp_auth_token"; +const char kOAuthRefreshTokenConfigPath[] = "oauth_refresh_token"; const char kXmppAuthServiceConfigPath[] = "xmpp_auth_service"; const char kHostIdConfigPath[] = "host_id"; const char kHostNameConfigPath[] = "host_name"; diff --git a/remoting/host/host_config.h b/remoting/host/host_config.h index 79b2060..4c7ed77 100644 --- a/remoting/host/host_config.h +++ b/remoting/host/host_config.h @@ -19,6 +19,8 @@ extern const char kHostEnabledConfigPath[]; extern const char kXmppLoginConfigPath[]; // Auth token used to authenticate to XMPP network. extern const char kXmppAuthTokenConfigPath[]; +// OAuth refresh token used to fetch an access token for the XMPP network. +extern const char kOAuthRefreshTokenConfigPath[]; // Auth service used to authenticate to XMPP network. extern const char kXmppAuthServiceConfigPath[]; // Unique identifier of the host used to register the host in directory. diff --git a/remoting/host/oauth_client.cc b/remoting/host/oauth_client.cc index 342b0eb..fdecdae 100644 --- a/remoting/host/oauth_client.cc +++ b/remoting/host/oauth_client.cc @@ -4,6 +4,7 @@ #include "remoting/host/oauth_client.h" +#include "base/bind.h" #include "remoting/host/chromoting_host_context.h" #include "remoting/host/url_request_context.h" @@ -11,7 +12,8 @@ namespace remoting { OAuthClient::OAuthClient() : network_thread_("OAuthNetworkThread"), - file_thread_("OAuthFileThread") { + file_thread_("OAuthFileThread"), + delegate_(NULL) { network_thread_.StartWithOptions( base::Thread::Options(MessageLoop::TYPE_IO, 0)); file_thread_.StartWithOptions( @@ -25,8 +27,50 @@ OAuthClient::OAuthClient() OAuthClient::~OAuthClient() { } -void OAuthClient::GetAccessToken(const std::string& refresh_token, - GaiaOAuthClient::Delegate* delegate) { +void OAuthClient::Start(const std::string& refresh_token, + OAuthClient::Delegate* delegate, + base::MessageLoopProxy* message_loop) { + refresh_token_ = refresh_token; + delegate_ = delegate; + message_loop_ = message_loop; + RefreshToken(); +} + +void OAuthClient::OnGetTokensResponse(const std::string& refresh_token, + const std::string& access_token, + int expires_in_seconds) { + NOTREACHED(); +} + +void OAuthClient::OnRefreshTokenResponse(const std::string& access_token, + int expires_in_seconds) { + message_loop_->PostTask( + FROM_HERE, + base::Bind(&OAuthClient::Delegate::OnRefreshTokenResponse, + base::Unretained(delegate_), + access_token, expires_in_seconds)); + // Queue a token exchange for 1 minute before this one expires. + message_loop_->PostDelayedTask(FROM_HERE, + base::Bind(&OAuthClient::RefreshToken, + base::Unretained(this)), + base::TimeDelta::FromSeconds( + expires_in_seconds - 60)); +} + +void OAuthClient::OnOAuthError() { + message_loop_->PostTask( + FROM_HERE, + base::Bind(&OAuthClient::Delegate::OnOAuthError, + base::Unretained(delegate_))); +} + +void OAuthClient::OnNetworkError(int response_code) { + // TODO(jamiewalch): Set a sensible retry limit and integrate with + // SignalingConnector to restart the reconnection process (crbug.com/118928). + NOTREACHED(); +} + +void OAuthClient::RefreshToken() { #ifdef OFFICIAL_BUILD OAuthClientInfo client_info = { "440925447803-avn2sj1kc099s0r7v62je5s339mu0am1.apps.googleusercontent.com", @@ -38,7 +82,7 @@ void OAuthClient::GetAccessToken(const std::string& refresh_token, "W2ieEsG-R1gIA4MMurGrgMc_" }; #endif - gaia_oauth_client_->RefreshToken(client_info, refresh_token, -1, delegate); + gaia_oauth_client_->RefreshToken(client_info, refresh_token_, -1, this); } } // namespace remoting diff --git a/remoting/host/oauth_client.h b/remoting/host/oauth_client.h index c377733..aa8b220 100644 --- a/remoting/host/oauth_client.h +++ b/remoting/host/oauth_client.h @@ -14,23 +14,59 @@ #include "remoting/host/gaia_oauth_client.h" #include "remoting/host/url_request_context.h" +namespace base { +class MessageLoopProxy; +} // namespace base + namespace remoting { -class OAuthClient { +class OAuthClient : public GaiaOAuthClient::Delegate { public: + class Delegate { + public: + virtual ~Delegate() { } + // Invoked on a successful response to the RefreshToken request. + virtual void OnRefreshTokenResponse(const std::string& access_token, + int expires_in_seconds) = 0; + // Invoked when there is an OAuth error with one of the requests. + virtual void OnOAuthError() = 0; + }; + OAuthClient(); - ~OAuthClient(); + virtual ~OAuthClient(); - void GetAccessToken(const std::string& refresh_token, - GaiaOAuthClient::Delegate* delegate); + // Notify the delegate on the speficied message loop when an access token is + // available. As long as the refresh is successful, another will be queued, + // timed just before the access token expires. As soon as an error occurs, + // this automatic refresh behaviour is cancelled. + // + // The delegate is accessed on the specified message loop, and must out-live + // it. + void Start(const std::string& refresh_token, + Delegate* delegate, + base::MessageLoopProxy* message_loop); + + // Overridden from GaiaOAuthClient::Delegate + virtual void OnGetTokensResponse(const std::string& refresh_token, + const std::string& access_token, + int expires_in_seconds) OVERRIDE; + virtual void OnRefreshTokenResponse(const std::string& access_token, + int expires) OVERRIDE; + virtual void OnOAuthError() OVERRIDE; + virtual void OnNetworkError(int response_code) OVERRIDE; private: + void RefreshToken(); + // TODO(jamiewalch): Move these to the ChromotingHostContext class so // that the URLRequestContextGetter is available for other purposes. base::Thread network_thread_; base::Thread file_thread_; scoped_ptr<URLRequestContextGetter> url_request_context_getter_; scoped_ptr<GaiaOAuthClient> gaia_oauth_client_; + std::string refresh_token_; + OAuthClient::Delegate* delegate_; + scoped_refptr<base::MessageLoopProxy> message_loop_; DISALLOW_COPY_AND_ASSIGN(OAuthClient); }; diff --git a/remoting/host/remoting_me2me_host.cc b/remoting/host/remoting_me2me_host.cc index 693150f..0c2c234 100644 --- a/remoting/host/remoting_me2me_host.cc +++ b/remoting/host/remoting_me2me_host.cc @@ -36,6 +36,7 @@ #include "remoting/host/host_event_logger.h" #include "remoting/host/json_host_config.h" #include "remoting/host/log_to_server.h" +#include "remoting/host/oauth_client.h" #include "remoting/host/policy_hack/nat_policy.h" #include "remoting/host/signaling_connector.h" #include "remoting/jingle_glue/xmpp_signal_strategy.h" @@ -95,7 +96,7 @@ FilePath GetDefaultConfigDir() { namespace remoting { -class HostProcess { +class HostProcess : public OAuthClient::Delegate { public: HostProcess() : message_loop_(MessageLoop::TYPE_UI), @@ -132,23 +133,56 @@ class HostProcess { } int Run() { - if (!LoadConfig(file_io_thread_.message_loop_proxy())) { + bool tokens_pending = false; + if (!LoadConfig(file_io_thread_.message_loop_proxy(), &tokens_pending)) { return 1; } - - nat_policy_.reset( - policy_hack::NatPolicy::Create(file_io_thread_.message_loop_proxy())); - nat_policy_->StartWatching( - base::Bind(&HostProcess::OnNatPolicyUpdate, base::Unretained(this))); + if (tokens_pending) { + // If we have an OAuth refresh token, then XmppSignalStrategy can't + // handle it directly, so refresh it asynchronously. A task will be + // posted on the message loop to start watching the NAT policy when + // the access token is available. + oauth_client_.Start(oauth_refresh_token_, this, + message_loop_.message_loop_proxy()); + } else { + StartWatchingNatPolicy(); + } message_loop_.Run(); return 0; } + // Overridden from OAuthClient::Delegate + virtual void OnRefreshTokenResponse(const std::string& access_token, + int expires) OVERRIDE { + xmpp_auth_token_ = access_token; + // If there's already a signal strategy object, update it ready for the + // next time it calls Connect. If not, then this is the initial token + // exchange, so proceed to the next stage of connection. + if (signal_strategy_.get()) { + signal_strategy_->SetAuthInfo(xmpp_login_, xmpp_auth_token_, + xmpp_auth_service_); + } else { + StartWatchingNatPolicy(); + } + } + + virtual void OnOAuthError() OVERRIDE { + LOG(ERROR) << "OAuth: invalid credentials."; + } + private: + void StartWatchingNatPolicy() { + nat_policy_.reset( + policy_hack::NatPolicy::Create(file_io_thread_.message_loop_proxy())); + nat_policy_->StartWatching( + base::Bind(&HostProcess::OnNatPolicyUpdate, base::Unretained(this))); + } + // Read Host config from disk, returning true if successful. - bool LoadConfig(base::MessageLoopProxy* io_message_loop) { + bool LoadConfig(base::MessageLoopProxy* io_message_loop, + bool* tokens_pending) { scoped_refptr<JsonHostConfig> host_config = new JsonHostConfig(host_config_path_, io_message_loop); scoped_refptr<JsonHostConfig> auth_config = @@ -187,20 +221,24 @@ class HostProcess { // Use an XMPP connection to the Talk network for session signalling. if (!auth_config->GetString(kXmppLoginConfigPath, &xmpp_login_) || - !auth_config->GetString(kXmppAuthTokenConfigPath, &xmpp_auth_token_)) { + !(auth_config->GetString(kXmppAuthTokenConfigPath, &xmpp_auth_token_) || + auth_config->GetString(kOAuthRefreshTokenConfigPath, + &oauth_refresh_token_))) { LOG(ERROR) << "XMPP credentials are not defined in the config."; return false; } - if (!auth_config->GetString(kXmppAuthServiceConfigPath, + *tokens_pending = oauth_refresh_token_ != ""; + if (*tokens_pending) { + xmpp_auth_token_ = ""; // This will be set to the access token later. + xmpp_auth_service_ = "oauth2"; + } else if (!auth_config->GetString(kXmppAuthServiceConfigPath, &xmpp_auth_service_)) { - // For the me2me host, we assume we use the ClientLogin token for - // chromiumsync because we do not have an HTTP stack with which we can - // easily request an OAuth2 access token even if we had a RefreshToken for - // the account. + // For the me2me host, we default to ClientLogin token for chromiumsync + // because earlier versions of the host had no HTTP stack with which to + // request an OAuth2 access token. xmpp_auth_service_ = kChromotingTokenDefaultServiceName; } - return true; } @@ -307,12 +345,15 @@ class HostProcess { std::string xmpp_auth_token_; std::string xmpp_auth_service_; + std::string oauth_refresh_token_; + OAuthClient oauth_client_; + scoped_ptr<policy_hack::NatPolicy> nat_policy_; bool allow_nat_traversal_; bool restarting_; - scoped_ptr<SignalStrategy> signal_strategy_; + scoped_ptr<XmppSignalStrategy> signal_strategy_; scoped_ptr<SignalingConnector> signaling_connector_; scoped_ptr<DesktopEnvironment> desktop_environment_; scoped_ptr<HeartbeatSender> heartbeat_sender_; |