Chromium Code Reviews| Index: remoting/host/url_fetcher_token_validator_factory.cc |
| diff --git a/remoting/host/url_fetcher_token_validator_factory.cc b/remoting/host/url_fetcher_token_validator_factory.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..1f04b9b23e9a0d7c4d2c0d86073e3b0a4310b4c2 |
| --- /dev/null |
| +++ b/remoting/host/url_fetcher_token_validator_factory.cc |
| @@ -0,0 +1,181 @@ |
| +// Copyright 2013 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "remoting/host/url_fetcher_token_validator_factory.h" |
| + |
| +#include <set> |
| + |
| +#include "base/base64.h" |
| +#include "base/bind.h" |
| +#include "base/callback.h" |
| +#include "base/json/json_reader.h" |
| +#include "base/location.h" |
| +#include "base/logging.h" |
| +#include "base/single_thread_task_runner.h" |
| +#include "base/values.h" |
| +#include "crypto/random.h" |
| +#include "googleurl/src/gurl.h" |
| +#include "net/base/escape.h" |
| +#include "net/url_request/url_fetcher.h" |
| +#include "net/url_request/url_fetcher_delegate.h" |
| +#include "net/url_request/url_request_status.h" |
| +#include "remoting/base/rsa_key_pair.h" |
| + |
| +namespace { |
| + |
| + // Length in bytes of the cryptographic nonce used to salt the token scope. |
| + const size_t kNonceLength = 16; // 128 bits. |
|
Sergey Ulanov
2013/03/28 22:34:54
indentation
rmsousa
2013/04/04 22:13:43
Done.
|
| + |
| +} |
| + |
| +namespace remoting { |
| + |
| +class UrlFetcherTokenValidator |
| + : public net::URLFetcherDelegate, |
| + public protocol::ThirdPartyHostAuthenticator::TokenValidator { |
| + public: |
| + UrlFetcherTokenValidator( |
| + const GURL& token_url, |
| + const GURL& token_validation_url, |
| + scoped_refptr<RsaKeyPair> key_pair, |
| + const std::string& local_jid, |
| + const std::string& remote_jid, |
| + scoped_refptr<net::URLRequestContextGetter> request_context_getter) |
| + : token_url_(token_url), |
| + token_validation_url_(token_validation_url), |
| + key_pair_(key_pair), |
| + request_context_getter_(request_context_getter) { |
| + DCHECK(token_url_.is_valid()); |
| + DCHECK(token_validation_url_.is_valid()); |
| + DCHECK(key_pair_); |
| + token_scope_ = CreateScope(local_jid, remote_jid); |
| + } |
| + |
| + virtual ~UrlFetcherTokenValidator() { |
| + } |
| + |
| + // TokenValidator interface. |
| + virtual void ValidateThirdPartyToken( |
| + const std::string& token, |
| + const base::Callback<void( |
| + const std::string& shared_secret)>& on_token_validated) OVERRIDE { |
| + DCHECK(!request_); |
| + DCHECK(!on_token_validated.is_null()); |
| + |
| + on_token_validated_ = on_token_validated; |
| + |
| + std::string post_body = |
| + "code=" + net::EscapeUrlEncodedData(token, true) + |
| + "&client_id=" + net::EscapeUrlEncodedData( |
| + key_pair_->GetPublicKey(), true) + |
| + "&client_secret=" + net::EscapeUrlEncodedData( |
| + key_pair_->SignMessage(token), true) + |
| + "&grant_type=authorization_code"; |
| + request_.reset(net::URLFetcher::Create( |
| + token_validation_url_, net::URLFetcher::POST, this)); |
| + request_->SetUploadData("application/x-www-form-urlencoded", post_body); |
|
Sergey Ulanov
2013/03/28 22:34:54
Can we use json here instead of url-encoding the d
rmsousa
2013/03/28 23:12:49
I'm trying to follow the OAuth protocol, which use
Sergey Ulanov
2013/03/28 23:39:39
I'm not sure why we need to worry about OAuth here
rmsousa
2013/04/04 22:13:43
Yes, the protocol will be OAuth. It's possible to
|
| + request_->SetRequestContext(request_context_getter_); |
| + request_->Start(); |
| + } |
| + |
| + virtual const GURL& token_url() const OVERRIDE { |
| + return token_url_; |
| + } |
| + |
| + virtual const std::string& token_scope() const OVERRIDE { |
| + return token_scope_; |
| + } |
| + |
| + // URLFetcherDelegate interface. |
| + virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE { |
| + DCHECK_EQ(request_.get(), source); |
| + std::string shared_token = GetSharedSecretFromResponse(source); |
| + on_token_validated_.Run(shared_token); |
| + request_.reset(); |
| + } |
| + |
| + private: |
| + bool IsValidScope(const std::string& token_scope) { |
| + // TODO(rmsousa): Deal with reordering/subsets/supersets/aliases/etc. |
| + return token_scope == token_scope_; |
| + } |
| + |
| + static std::string CreateScope(const std::string& local_jid, |
| + const std::string& remote_jid) { |
| + char nonce_bytes[kNonceLength]; |
| + crypto::RandBytes(nonce_bytes, kNonceLength); |
| + std::string nonce; |
| + bool success = base::Base64Encode(nonce_bytes, &nonce); |
| + DCHECK(success); |
| + return "client:" + remote_jid + " host:" + local_jid + " nonce:" + nonce; |
| + } |
| + |
| + std::string GetSharedSecretFromResponse(const net::URLFetcher* source) { |
|
Sergey Ulanov
2013/03/28 22:34:54
Maybe call it ProcessResponse?
Sergey Ulanov
2013/03/28 22:34:54
|source| is a meaningless name in this context, bu
rmsousa
2013/04/04 22:13:43
Done.
rmsousa
2013/04/04 22:13:43
Done.
|
| + std::string shared_secret; |
| + |
| + // Verify that we got a successful response. |
| + int response = source->GetResponseCode(); |
| + net::URLRequestStatus status = source->GetStatus(); |
| + std::string data; |
| + if (!status.is_success() || response != 200) { |
| + LOG(ERROR) << |
|
Sergey Ulanov
2013/03/28 22:34:54
nit: wrap << operator
rmsousa
2013/04/04 22:13:43
Done.
|
| + "Error " << response << " validating token: '" << data << "'"; |
| + return shared_secret; |
|
Sergey Ulanov
2013/03/28 22:34:54
This is confusing. Maybe better to return std::str
rmsousa
2013/04/04 22:13:43
Done.
|
| + } |
| + |
| + // Decode the JSON data from the response. |
| + source->GetResponseAsString(&data); |
| + scoped_ptr<base::Value> value(base::JSONReader::Read(data)); |
| + if (!value.get() || value->GetType() != base::Value::TYPE_DICTIONARY) { |
| + LOG(ERROR) << "Invalid token validation response: '" << data << "'"; |
| + return shared_secret; |
| + } |
| + |
| + // Get the scope from the response, and verify that it is valid. |
| + DictionaryValue* dict = static_cast<DictionaryValue*>(value.get()); |
|
Sergey Ulanov
2013/03/28 22:34:54
Ouch. Instead of downcasting using static_cast<> u
rmsousa
2013/04/04 22:13:43
Done.
|
| + std::string token_scope; |
| + dict->GetStringWithoutPathExpansion("scope", &token_scope); |
| + if (!IsValidScope(token_scope)) { |
| + LOG(ERROR) << "Invalid scope: '" << token_scope |
| + << "', expected: '" << token_scope_ <<"'."; |
| + return shared_secret; |
| + } |
| + |
| + // Everything is valid, so return the shared secret to the caller. |
| + dict->GetStringWithoutPathExpansion("access_token", &shared_secret); |
| + return shared_secret; |
| + } |
| + |
| + scoped_ptr<net::URLFetcher> request_; |
| + GURL token_url_; |
| + GURL token_validation_url_; |
| + scoped_refptr<RsaKeyPair> key_pair_; |
| + std::string token_scope_; |
| + scoped_refptr<net::URLRequestContextGetter> request_context_getter_; |
| + base::Callback<void(const std::string& shared_secret)> on_token_validated_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(UrlFetcherTokenValidator); |
| +}; |
| + |
| +UrlFetcherTokenValidatorFactory::UrlFetcherTokenValidatorFactory( |
| + scoped_refptr<net::URLRequestContextGetter> request_context_getter) |
| + : request_context_getter_(request_context_getter) { |
| +} |
| + |
| +UrlFetcherTokenValidatorFactory::~UrlFetcherTokenValidatorFactory() { |
| +} |
| + |
| +scoped_ptr<protocol::ThirdPartyHostAuthenticator::TokenValidator> |
| +UrlFetcherTokenValidatorFactory::CreateTokenValidator( |
| + const GURL& token_url, |
| + const GURL& token_validation_url, |
| + scoped_refptr<RsaKeyPair> key_pair, |
| + const std::string& local_jid, |
| + const std::string& remote_jid) { |
| + return scoped_ptr<protocol::ThirdPartyHostAuthenticator::TokenValidator>( |
| + new UrlFetcherTokenValidator(token_url, token_validation_url, key_pair, |
| + local_jid, remote_jid, request_context_getter_)); |
|
Sergey Ulanov
2013/03/28 22:34:54
indentation
rmsousa
2013/04/04 22:13:43
Done.
|
| +} |
| + |
| +} // namespace remoting |