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.
5 #include "remoting/host/token_validator_factory_impl.h"
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"
28 // Length in bytes of the cryptographic nonce used to salt the token scope.
29 const size_t kNonceLength
= 16; // 128 bits.
35 class TokenValidatorImpl
36 : public net::URLFetcherDelegate
,
37 public protocol::ThirdPartyHostAuthenticator::TokenValidator
{
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
),
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
);
56 virtual ~TokenValidatorImpl() {
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
{
65 DCHECK(!on_token_validated
.is_null());
67 on_token_validated_
= on_token_validated
;
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());
83 virtual const GURL
& token_url() const OVERRIDE
{
87 virtual const std::string
& token_scope() const OVERRIDE
{
91 // URLFetcherDelegate interface.
92 virtual void OnURLFetchComplete(const net::URLFetcher
* source
) OVERRIDE
{
93 DCHECK_EQ(request_
.get(), source
);
94 std::string shared_token
= ProcessResponse();
96 on_token_validated_
.Run(shared_token
);
100 bool IsValidScope(const std::string
& token_scope
) {
101 // TODO(rmsousa): Deal with reordering/subsets/supersets/aliases/etc.
102 return token_scope
== token_scope_
;
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
);
110 bool success
= base::Base64Encode(nonce_bytes
, &nonce
);
112 return "client:" + remote_jid
+ " host:" + local_jid
+ " nonce:" + nonce
;
115 std::string
ProcessResponse() {
116 // Verify that we got a successful response.
117 net::URLRequestStatus status
= request_
->GetStatus();
118 if (!status
.is_success()) {
119 LOG(ERROR
) << "Error validating token, status=" << status
.status()
120 << " err=" << status
.error();
121 return std::string();
124 int response
= request_
->GetResponseCode();
126 request_
->GetResponseAsString(&data
);
127 if (response
!= 200) {
129 << "Error " << response
<< " validating token: '" << data
<< "'";
130 return std::string();
133 // Decode the JSON data from the response.
134 scoped_ptr
<base::Value
> value(base::JSONReader::Read(data
));
135 DictionaryValue
* dict
;
136 if (!value
.get() || value
->GetType() != base::Value::TYPE_DICTIONARY
||
137 !value
->GetAsDictionary(&dict
)) {
138 LOG(ERROR
) << "Invalid token validation response: '" << data
<< "'";
139 return std::string();
142 std::string token_scope
;
143 dict
->GetStringWithoutPathExpansion("scope", &token_scope
);
144 if (!IsValidScope(token_scope
)) {
145 LOG(ERROR
) << "Invalid scope: '" << token_scope
146 << "', expected: '" << token_scope_
<<"'.";
147 return std::string();
150 std::string shared_secret
;
151 // Everything is valid, so return the shared secret to the caller.
152 dict
->GetStringWithoutPathExpansion("access_token", &shared_secret
);
153 return shared_secret
;
156 scoped_ptr
<net::URLFetcher
> request_
;
158 GURL token_validation_url_
;
159 scoped_refptr
<RsaKeyPair
> key_pair_
;
160 std::string token_scope_
;
161 scoped_refptr
<net::URLRequestContextGetter
> request_context_getter_
;
162 base::Callback
<void(const std::string
& shared_secret
)> on_token_validated_
;
164 DISALLOW_COPY_AND_ASSIGN(TokenValidatorImpl
);
167 TokenValidatorFactoryImpl::TokenValidatorFactoryImpl(
168 const GURL
& token_url
,
169 const GURL
& token_validation_url
,
170 scoped_refptr
<RsaKeyPair
> key_pair
,
171 scoped_refptr
<net::URLRequestContextGetter
> request_context_getter
)
172 : token_url_(token_url
),
173 token_validation_url_(token_validation_url
),
175 request_context_getter_(request_context_getter
) {
178 TokenValidatorFactoryImpl::~TokenValidatorFactoryImpl() {
181 scoped_ptr
<protocol::ThirdPartyHostAuthenticator::TokenValidator
>
182 TokenValidatorFactoryImpl::CreateTokenValidator(
183 const std::string
& local_jid
,
184 const std::string
& remote_jid
) {
185 return scoped_ptr
<protocol::ThirdPartyHostAuthenticator::TokenValidator
>(
186 new TokenValidatorImpl(token_url_
, token_validation_url_
, key_pair_
,
187 local_jid
, remote_jid
,
188 request_context_getter_
));
191 } // namespace remoting