token_validator_factory_impl.cc revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
1// Copyright 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "remoting/host/token_validator_factory_impl.h" 6 7#include <set> 8 9#include "base/base64.h" 10#include "base/bind.h" 11#include "base/callback.h" 12#include "base/json/json_reader.h" 13#include "base/location.h" 14#include "base/logging.h" 15#include "base/single_thread_task_runner.h" 16#include "base/strings/string_util.h" 17#include "base/values.h" 18#include "crypto/random.h" 19#include "net/base/escape.h" 20#include "net/url_request/url_fetcher.h" 21#include "net/url_request/url_fetcher_delegate.h" 22#include "net/url_request/url_request_status.h" 23#include "remoting/base/rsa_key_pair.h" 24#include "url/gurl.h" 25 26namespace { 27 28// Length in bytes of the cryptographic nonce used to salt the token scope. 29const size_t kNonceLength = 16; // 128 bits. 30 31} 32 33namespace remoting { 34 35class TokenValidatorImpl 36 : public net::URLFetcherDelegate, 37 public protocol::ThirdPartyHostAuthenticator::TokenValidator { 38 public: 39 TokenValidatorImpl( 40 const GURL& token_url, 41 const GURL& token_validation_url, 42 scoped_refptr<RsaKeyPair> key_pair, 43 const std::string& local_jid, 44 const std::string& remote_jid, 45 scoped_refptr<net::URLRequestContextGetter> request_context_getter) 46 : token_url_(token_url), 47 token_validation_url_(token_validation_url), 48 key_pair_(key_pair), 49 request_context_getter_(request_context_getter) { 50 DCHECK(token_url_.is_valid()); 51 DCHECK(token_validation_url_.is_valid()); 52 DCHECK(key_pair_.get()); 53 token_scope_ = CreateScope(local_jid, remote_jid); 54 } 55 56 virtual ~TokenValidatorImpl() { 57 } 58 59 // TokenValidator interface. 60 virtual void ValidateThirdPartyToken( 61 const std::string& token, 62 const base::Callback<void( 63 const std::string& shared_secret)>& on_token_validated) OVERRIDE { 64 DCHECK(!request_); 65 DCHECK(!on_token_validated.is_null()); 66 67 on_token_validated_ = on_token_validated; 68 69 std::string post_body = 70 "code=" + net::EscapeUrlEncodedData(token, true) + 71 "&client_id=" + net::EscapeUrlEncodedData( 72 key_pair_->GetPublicKey(), true) + 73 "&client_secret=" + net::EscapeUrlEncodedData( 74 key_pair_->SignMessage(token), true) + 75 "&grant_type=authorization_code"; 76 request_.reset(net::URLFetcher::Create( 77 0, token_validation_url_, net::URLFetcher::POST, this)); 78 request_->SetUploadData("application/x-www-form-urlencoded", post_body); 79 request_->SetRequestContext(request_context_getter_.get()); 80 request_->Start(); 81 } 82 83 virtual const GURL& token_url() const OVERRIDE { 84 return token_url_; 85 } 86 87 virtual const std::string& token_scope() const OVERRIDE { 88 return token_scope_; 89 } 90 91 // URLFetcherDelegate interface. 92 virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE { 93 DCHECK_EQ(request_.get(), source); 94 std::string shared_token = ProcessResponse(); 95 request_.reset(); 96 on_token_validated_.Run(shared_token); 97 } 98 99 private: 100 bool IsValidScope(const std::string& token_scope) { 101 // TODO(rmsousa): Deal with reordering/subsets/supersets/aliases/etc. 102 return token_scope == token_scope_; 103 } 104 105 static std::string CreateScope(const std::string& local_jid, 106 const std::string& remote_jid) { 107 std::string nonce_bytes; 108 crypto::RandBytes(WriteInto(&nonce_bytes, kNonceLength + 1), kNonceLength); 109 std::string nonce; 110 base::Base64Encode(nonce_bytes, &nonce); 111 return "client:" + remote_jid + " host:" + local_jid + " nonce:" + nonce; 112 } 113 114 std::string ProcessResponse() { 115 // Verify that we got a successful response. 116 net::URLRequestStatus status = request_->GetStatus(); 117 if (!status.is_success()) { 118 LOG(ERROR) << "Error validating token, status=" << status.status() 119 << " err=" << status.error(); 120 return std::string(); 121 } 122 123 int response = request_->GetResponseCode(); 124 std::string data; 125 request_->GetResponseAsString(&data); 126 if (response != 200) { 127 LOG(ERROR) 128 << "Error " << response << " validating token: '" << data << "'"; 129 return std::string(); 130 } 131 132 // Decode the JSON data from the response. 133 scoped_ptr<base::Value> value(base::JSONReader::Read(data)); 134 DictionaryValue* dict; 135 if (!value.get() || value->GetType() != base::Value::TYPE_DICTIONARY || 136 !value->GetAsDictionary(&dict)) { 137 LOG(ERROR) << "Invalid token validation response: '" << data << "'"; 138 return std::string(); 139 } 140 141 std::string token_scope; 142 dict->GetStringWithoutPathExpansion("scope", &token_scope); 143 if (!IsValidScope(token_scope)) { 144 LOG(ERROR) << "Invalid scope: '" << token_scope 145 << "', expected: '" << token_scope_ <<"'."; 146 return std::string(); 147 } 148 149 std::string shared_secret; 150 // Everything is valid, so return the shared secret to the caller. 151 dict->GetStringWithoutPathExpansion("access_token", &shared_secret); 152 return shared_secret; 153 } 154 155 scoped_ptr<net::URLFetcher> request_; 156 GURL token_url_; 157 GURL token_validation_url_; 158 scoped_refptr<RsaKeyPair> key_pair_; 159 std::string token_scope_; 160 scoped_refptr<net::URLRequestContextGetter> request_context_getter_; 161 base::Callback<void(const std::string& shared_secret)> on_token_validated_; 162 163 DISALLOW_COPY_AND_ASSIGN(TokenValidatorImpl); 164}; 165 166TokenValidatorFactoryImpl::TokenValidatorFactoryImpl( 167 const GURL& token_url, 168 const GURL& token_validation_url, 169 scoped_refptr<RsaKeyPair> key_pair, 170 scoped_refptr<net::URLRequestContextGetter> request_context_getter) 171 : token_url_(token_url), 172 token_validation_url_(token_validation_url), 173 key_pair_(key_pair), 174 request_context_getter_(request_context_getter) { 175} 176 177TokenValidatorFactoryImpl::~TokenValidatorFactoryImpl() { 178} 179 180scoped_ptr<protocol::ThirdPartyHostAuthenticator::TokenValidator> 181TokenValidatorFactoryImpl::CreateTokenValidator( 182 const std::string& local_jid, 183 const std::string& remote_jid) { 184 return scoped_ptr<protocol::ThirdPartyHostAuthenticator::TokenValidator>( 185 new TokenValidatorImpl(token_url_, token_validation_url_, key_pair_, 186 local_jid, remote_jid, 187 request_context_getter_)); 188} 189 190} // namespace remoting 191