summaryrefslogtreecommitdiffstats
path: root/remoting
diff options
context:
space:
mode:
authorjamiewalch@chromium.org <jamiewalch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-03-20 20:08:40 +0000
committerjamiewalch@chromium.org <jamiewalch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-03-20 20:08:40 +0000
commitb8f85978f9e45f66eec6b8581b8ae54f46014da0 (patch)
tree5fd56df69a5255519e591e2367b89d0dd3f91921 /remoting
parent05454a435deac9bce39960ea21e218ebde7d17b5 (diff)
downloadchromium_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.cc1
-rw-r--r--remoting/host/host_config.h2
-rw-r--r--remoting/host/oauth_client.cc52
-rw-r--r--remoting/host/oauth_client.h44
-rw-r--r--remoting/host/remoting_me2me_host.cc73
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_;