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