Skip to content

Commit 7e0207e

Browse files
author
Valtteri Heikkila
committed
Added temporary token flag in oauth1_token. Added OAuth Core 1.0 unit test and cleaned up the tests. Fixed more issues in oauth1/2client samples.
1 parent 91f1f72 commit 7e0207e

File tree

5 files changed

+133
-48
lines changed

5 files changed

+133
-48
lines changed

Release/include/cpprest/oauth1_handler.h

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -95,23 +95,35 @@ class oauth1_exception : public std::exception
9595
class oauth1_token
9696
{
9797
public:
98-
oauth1_token(utility::string_t token=utility::string_t(),
99-
utility::string_t secret=utility::string_t()) :
98+
oauth1_token(utility::string_t token, utility::string_t secret, bool is_temporary) :
10099
m_token(token),
101-
m_secret(secret)
100+
m_secret(secret),
101+
m_is_temporary(is_temporary)
102102
{}
103103

104-
bool is_valid() const { return !(token().empty() || secret().empty()); }
104+
oauth1_token() :
105+
m_is_temporary(false)
106+
{}
107+
108+
bool is_valid() const { return !(is_temporary() || token().empty() || secret().empty()); }
105109

106110
const utility::string_t& token() const { return m_token; }
107111
void set_token(utility::string_t token) { m_token = std::move(token); }
108112

109113
const utility::string_t& secret() const { return m_secret; }
110114
void set_secret(utility::string_t secret) { m_secret = std::move(secret); }
111115

116+
bool is_temporary() const { return m_is_temporary; }
117+
/// <summary>
118+
/// If set, token is a temporary token. Note that is_valid() will also return
119+
/// false if this option is set.
120+
/// </summary>
121+
void set_is_temporary(bool is_temporary) { m_is_temporary = std::move(is_temporary); }
122+
112123
private:
113124
utility::string_t m_token;
114125
utility::string_t m_secret;
126+
bool m_is_temporary;
115127
};
116128

117129

@@ -218,15 +230,25 @@ class oauth1_config
218230

219231
/// <summary>
220232
/// Creates a task to fetch token from the token endpoint.
221-
/// The task creates a HTTP request to the token_endpoint() which is
222-
/// used exchange a verifier to an access token.
233+
/// Behavior depends on the use_core10() setting.
234+
/// If use_core10() is not set, the request will exchange a verifier code
235+
/// to an access token. Otherwise verifier is omitted and current (temporary)
236+
/// token is used instead in the exchange.
223237
/// If successful, resulting token is set as active via set_token().
224238
/// See: http://tools.ietf.org/html/rfc5849#section-2.3
225239
/// </summary>
226240
/// <param name="verifier">Verifier received via redirect upon successful authorization.</param>
227241
pplx::task<void> token_from_verifier(utility::string_t verifier)
228242
{
229-
return _request_token(_generate_auth_state(oauth1_strings::verifier, uri::encode_data_string(verifier)), false);
243+
if (!use_core10())
244+
{
245+
return _request_token(_generate_auth_state(oauth1_strings::verifier, uri::encode_data_string(verifier)), false);
246+
}
247+
else
248+
{
249+
// Do not use verifier in obsolete OAuth Core 1.0.
250+
return _request_token(_generate_auth_state(), false);
251+
}
230252
}
231253

232254
const utility::string_t& consumer_key() const { return m_consumer_key; }

Release/samples/Oauth1Client/Oauth1Client.cpp

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,9 @@ class oauth1_code_listener
9999
m_listener->support([this](http::http_request request) -> void
100100
{
101101
pplx::extensibility::scoped_critical_section_t lck(m_resplock);
102-
m_config.token_from_redirected_uri(request.request_uri()).then([this,request](pplx::task<void> token_task) -> void
102+
if (request.request_uri().path() == U("/") && request.request_uri().query() != U(""))
103103
{
104-
if (request.request_uri().path() == U("/") && request.request_uri().query() != U(""))
104+
m_config.token_from_redirected_uri(request.request_uri()).then([this,request](pplx::task<void> token_task) -> void
105105
{
106106
try
107107
{
@@ -113,10 +113,14 @@ class oauth1_code_listener
113113
ucout << "Error: " << e.what() << std::endl;
114114
m_tce.set(false);
115115
}
116-
}
117-
});
118-
119-
request.reply(status_codes::OK, U("Ok."));
116+
});
117+
118+
request.reply(status_codes::OK, U("Ok."));
119+
}
120+
else
121+
{
122+
request.reply(status_codes::NotFound, U("Not found."));
123+
}
120124
});
121125
m_listener->open().wait();
122126
}

Release/samples/Oauth2Client/Oauth2Client.cpp

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -99,24 +99,28 @@ class oauth2_code_listener
9999
m_listener->support([this](http::http_request request) -> void
100100
{
101101
pplx::extensibility::scoped_critical_section_t lck(m_resplock);
102-
m_config.token_from_redirected_uri(request.request_uri()).then([this,request](pplx::task<void>token_task) -> void
102+
if (request.request_uri().path() == U("/") && request.request_uri().query() != U(""))
103103
{
104-
if (request.request_uri().path() == U("/") && request.request_uri().query() != U(""))
104+
m_config.token_from_redirected_uri(request.request_uri()).then([this,request](pplx::task<void> token_task) -> void
105105
{
106106
try
107107
{
108108
token_task.wait();
109109
m_tce.set(true);
110110
}
111-
catch (oauth1_exception& e)
111+
catch (oauth2_exception& e)
112112
{
113113
ucout << "Error: " << e.what() << std::endl;
114114
m_tce.set(false);
115115
}
116-
}
117-
});
116+
});
118117

119-
request.reply(status_codes::OK, U("Ok."));
118+
request.reply(status_codes::OK, U("Ok."));
119+
}
120+
else
121+
{
122+
request.reply(status_codes::NotFound, U("Not found."));
123+
}
120124
});
121125
m_listener->open().wait();
122126
}

Release/src/http/oauth/oauth1_handler.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ pplx::task<void> oauth1_config::_request_token(oauth1_auth_state state, bool is_
305305
}
306306

307307
// Set the token even if the token is a temp token.
308-
set_token(oauth1_token(token_param->second, token_secret_param->second));
308+
set_token(oauth1_token(token_param->second, token_secret_param->second, is_temp_token_request));
309309
});
310310
}
311311

@@ -383,20 +383,21 @@ pplx::task<void> oauth1_config::token_from_redirected_uri(web::http::uri redirec
383383
return pplx::task_from_exception<void>(oauth1_exception(err.str().c_str()));
384384
}
385385

386+
utility::string_t verifier;
386387
if (!use_core10())
387388
{
388389
auto verifier_param = query.find(oauth1_strings::verifier);
389390
if (verifier_param == query.end())
390391
{
391392
return pplx::task_from_exception<void>(oauth1_exception(U("parameter 'oauth_verifier' missing from redirected URI.")));
392393
}
393-
return token_from_verifier(verifier_param->second);
394+
verifier = verifier_param->second;
394395
}
395396
else
396397
{
397-
// Obsolete OAuth Core 1.0 does not require 'oauth_verifier'. Instead it uses 'oauth_token'.
398-
return _request_token(_generate_auth_state(), false);
398+
// Do not set verifier since obsolete OAuth Core 1.0 does not use 'oauth_verifier'.
399399
}
400+
return token_from_verifier(verifier);
400401
}
401402

402403

Release/tests/Functional/http/client/oauth1_tests.cpp

Lines changed: 79 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,9 @@ SUITE(oauth1_tests)
4343

4444
struct oauth1_basic_config
4545
{
46-
// TODO: modify parameters (secret, token secret) to include encodable characters?
4746
oauth1_basic_config() :
4847
m_oauth1_config(U("test_key"), U("test_secret"),
49-
oauth1_token(U("test_token"), U("test_token_secret")),
48+
oauth1_token(U("test_token"), U("test_token_secret"), false),
5049
oauth1_methods::hmac_sha1),
5150
m_oauth1_handler(m_oauth1_config)
5251
{}
@@ -84,7 +83,7 @@ struct oauth1_auth_config_server : public oauth1_server_setup
8483
};
8584

8685

87-
// TODO: This test should be probably moved elsewhere.
86+
// TODO: This test should be probably moved elsewhere?
8887
TEST(oauth1_unique_nonces)
8988
{
9089
// Generate 100 nonces and check each is unique.
@@ -118,7 +117,7 @@ TEST_FIXTURE(oauth1_basic_config, oauth1_signature_base_string)
118117
));
119118
VERIFY_ARE_EQUAL(correct_base_string, base_string);
120119
}
121-
// Added extra_param and proper parameter normalization.
120+
// Added "extra_param" and proper parameter normalization.
122121
{
123122
http_request r;
124123
r.set_method(methods::POST);
@@ -150,13 +149,11 @@ TEST_FIXTURE(oauth1_basic_config, oauth1_hmac_sha1_method)
150149

151150
utility::string_t correct_signature(U("iUq3VlP39UNXoJHXlKjgSTmjEs8="));
152151
VERIFY_ARE_EQUAL(correct_signature, signature);
153-
154-
// TODO: verify extra_param is added correctly???
155152
}
156153

157154
TEST_FIXTURE(oauth1_basic_config, oauth1_rsa_sha1_method)
158155
{
159-
// TODO: verify signature over base string
156+
// TODO: not implemented
160157
}
161158

162159
TEST_FIXTURE(oauth1_basic_config, oauth1_plaintext_method)
@@ -168,8 +165,9 @@ TEST_FIXTURE(oauth1_basic_config, oauth1_plaintext_method)
168165

169166
TEST_FIXTURE(oauth1_basic_config_server, oauth1_hmac_sha1_request)
170167
{
171-
http_client_config client_config;
172168
m_oauth1_config.set_method(oauth1_methods::hmac_sha1);
169+
170+
http_client_config client_config;
173171
client_config.set_oauth1(m_oauth1_config);
174172
http_client client(m_server_uri, client_config);
175173

@@ -181,19 +179,21 @@ TEST_FIXTURE(oauth1_basic_config_server, oauth1_hmac_sha1_request)
181179
request->reply(status_codes::OK);
182180
});
183181

182+
VERIFY_IS_TRUE(m_oauth1_config.token().is_valid());
184183
http_response response = client.request(methods::GET).get();
185184
VERIFY_ARE_EQUAL(status_codes::OK, response.status_code());
186185
}
187186

188187
TEST_FIXTURE(oauth1_basic_config_server, oauth1_rsa_sha1_request)
189188
{
190-
// TODO: create test server, send request, verity headers are correct
189+
// TODO: not implemented
191190
}
192191

193192
TEST_FIXTURE(oauth1_basic_config_server, oauth1_plaintext_request)
194193
{
195-
http_client_config client_config;
196194
m_oauth1_config.set_method(oauth1_methods::plaintext);
195+
196+
http_client_config client_config;
197197
client_config.set_oauth1(m_oauth1_config);
198198
http_client client(m_server_uri, client_config);
199199

@@ -205,25 +205,22 @@ TEST_FIXTURE(oauth1_basic_config_server, oauth1_plaintext_request)
205205
request->reply(status_codes::OK);
206206
});
207207

208+
VERIFY_IS_TRUE(m_oauth1_config.token().is_valid());
208209
http_response response = client.request(methods::GET).get();
209210
VERIFY_ARE_EQUAL(status_codes::OK, response.status_code());
210211
}
211212

212213
TEST_FIXTURE(oauth1_auth_config_server, oauth1_build_authorization_uri)
213214
{
214-
http_client_config client_config;
215-
client_config.set_oauth1(m_oauth1_config);
216-
http_client client(m_server_uri, client_config);
217-
218215
m_server.server()->next_request().then([](test_request *request)
219216
{
220217
const utility::string_t header_authorization(request->m_headers[header_names::authorization]);
221218

222-
// Verify empty token.
219+
// Verify prefix, and without 'oauth_token'.
223220
const utility::string_t prefix(U("OAuth oauth_version=\"1.0\", oauth_consumer_key=\"test_key\", oauth_signature_method=\"HMAC-SHA1\", oauth_timestamp=\""));
224221
VERIFY_ARE_EQUAL(0, header_authorization.find(prefix));
225222

226-
// Verify 'oauth_callback'.
223+
// Verify suffix with proper 'oauth_callback'.
227224
const utility::string_t suffix(U(", oauth_callback=\"http%3A%2F%2Flocalhost%3A17778%2F\""));
228225
VERIFY_IS_TRUE(std::equal(suffix.rbegin(), suffix.rend(), header_authorization.rbegin()));
229226

@@ -233,45 +230,102 @@ TEST_FIXTURE(oauth1_auth_config_server, oauth1_build_authorization_uri)
233230
request->reply(status_codes::OK, U(""), headers, "oauth_token=foobar&oauth_token_secret=xyzzy&oauth_callback_confirmed=true");
234231
});
235232

233+
VERIFY_IS_FALSE(m_oauth1_config.token().is_temporary());
236234
utility::string_t auth_uri = m_oauth1_config.build_authorization_uri().get();
237235
VERIFY_ARE_EQUAL(auth_uri, U("http://localhost:17778/?oauth_token=foobar"));
236+
VERIFY_IS_TRUE(m_oauth1_config.token().is_temporary());
238237
}
239238

240239
// NOTE: This test also covers token_from_verifier().
241240
TEST_FIXTURE(oauth1_auth_config_server, oauth1_token_from_redirected_uri)
242241
{
243-
http_client_config client_config;
244-
client_config.set_oauth1(m_oauth1_config);
245-
http_client client(m_server_uri, client_config);
246-
247-
m_oauth1_config.set_token(oauth1_token(U("xyzzy"), U(""))); // Simulate temporary token.
248-
// TODO: Set verify token secret from signature?
249-
250242
m_server.server()->next_request().then([](test_request *request)
251243
{
252244
const utility::string_t header_authorization(request->m_headers[header_names::authorization]);
253245

254-
// Verify temp token.
246+
// Verify temporary token prefix.
255247
const utility::string_t prefix(U("OAuth oauth_version=\"1.0\", oauth_consumer_key=\"test_key\", oauth_token=\"xyzzy\", oauth_signature_method=\"HMAC-SHA1\", oauth_timestamp=\""));
256248
VERIFY_ARE_EQUAL(0, header_authorization.find(prefix));
257249

258-
// Verify 'oauth_verifier'.
250+
// Verify suffix with 'oauth_verifier'.
259251
const utility::string_t suffix(U(", oauth_verifier=\"simsalabim\""));
260252
VERIFY_IS_TRUE(std::equal(suffix.rbegin(), suffix.rend(), header_authorization.rbegin()));
261253

254+
// Verify we have 'oauth_nonce' and 'oauth_signature'.
255+
VERIFY_ARE_NOT_EQUAL(utility::string_t::npos, header_authorization.find(U("oauth_nonce")));
256+
VERIFY_ARE_NOT_EQUAL(utility::string_t::npos, header_authorization.find(U("oauth_signature")));
257+
262258
// Reply with access token and secret.
263259
std::map<utility::string_t, utility::string_t> headers;
264260
headers[header_names::content_type] = mime_types::application_x_www_form_urlencoded;
265261
request->reply(status_codes::OK, U(""), headers, "oauth_token=foo&oauth_token_secret=bar");
266262
});
267263

264+
m_oauth1_config.set_token(oauth1_token(U("xyzzy"), U(""), true)); // Simulate temporary token.
265+
VERIFY_IS_TRUE(m_oauth1_config.token().is_temporary());
266+
268267
const web::http::uri redirected_uri(U("http://localhost:17778/?oauth_token=xyzzy&oauth_verifier=simsalabim"));
269268
m_oauth1_config.token_from_redirected_uri(redirected_uri).wait();
270269

270+
VERIFY_IS_TRUE(m_oauth1_config.token().is_valid());
271271
VERIFY_ARE_EQUAL(m_oauth1_config.token().token(), U("foo"));
272272
VERIFY_ARE_EQUAL(m_oauth1_config.token().secret(), U("bar"));
273273
}
274274

275+
TEST_FIXTURE(oauth1_auth_config_server, oauth1_core10)
276+
{
277+
m_oauth1_config.set_use_core10(true);
278+
279+
// Verify authorization URI is without 'oauth_callback'.
280+
m_server.server()->next_request().then([](test_request *request)
281+
{
282+
const utility::string_t header_authorization(request->m_headers[header_names::authorization]);
283+
284+
// Verify no token in prefix.
285+
const utility::string_t prefix(U("OAuth oauth_version=\"1.0\", oauth_consumer_key=\"test_key\", oauth_signature_method=\"HMAC-SHA1\", oauth_timestamp=\""));
286+
VERIFY_ARE_EQUAL(0, header_authorization.find(prefix));
287+
288+
// Verify no 'oauth_callback'.
289+
VERIFY_ARE_EQUAL(utility::string_t::npos, header_authorization.find(U("oauth_callback")));
290+
291+
// Reply with temporary token and secret.
292+
std::map<utility::string_t, utility::string_t> headers;
293+
headers[header_names::content_type] = mime_types::application_x_www_form_urlencoded;
294+
request->reply(status_codes::OK, U(""), headers, "oauth_token=foobar&oauth_token_secret=xyzzy");
295+
});
296+
297+
VERIFY_IS_FALSE(m_oauth1_config.token().is_temporary());
298+
utility::string_t auth_uri = m_oauth1_config.build_authorization_uri().get();
299+
VERIFY_ARE_EQUAL(auth_uri, U("http://localhost:17778/?oauth_token=foobar&oauth_callback=http://localhost:17778/"));
300+
VERIFY_IS_TRUE(m_oauth1_config.token().is_temporary());
301+
302+
// Verify token request is sent without 'oauth_verifier' parameter.
303+
m_server.server()->next_request().then([](test_request *request)
304+
{
305+
const utility::string_t header_authorization(request->m_headers[header_names::authorization]);
306+
307+
// Verify we have token in prefix.
308+
const utility::string_t prefix(U("OAuth oauth_version=\"1.0\", oauth_consumer_key=\"test_key\", oauth_token=\"foobar\", oauth_signature_method=\"HMAC-SHA1\", oauth_timestamp=\""));
309+
VERIFY_ARE_EQUAL(0, header_authorization.find(prefix));
310+
311+
// Verify we have 'oauth_nonce' and 'oauth_signature'.
312+
VERIFY_ARE_NOT_EQUAL(utility::string_t::npos, header_authorization.find(U("oauth_nonce")));
313+
VERIFY_ARE_NOT_EQUAL(utility::string_t::npos, header_authorization.find(U("oauth_signature")));
314+
315+
// Reply with access token and secret.
316+
std::map<utility::string_t, utility::string_t> headers;
317+
headers[header_names::content_type] = mime_types::application_x_www_form_urlencoded;
318+
request->reply(status_codes::OK, U(""), headers, "oauth_token=baz&oauth_token_secret=123");
319+
});
320+
321+
VERIFY_IS_FALSE(m_oauth1_config.token().is_valid());
322+
const utility::string_t simulated_redirected_uri(U("http://localhost:17778/?oauth_token=foobar"));
323+
m_oauth1_config.token_from_redirected_uri(simulated_redirected_uri).wait();
324+
325+
VERIFY_IS_TRUE(m_oauth1_config.token().is_valid());
326+
VERIFY_ARE_EQUAL(m_oauth1_config.token().token(), U("baz"));
327+
VERIFY_ARE_EQUAL(m_oauth1_config.token().secret(), U("123"));
328+
}
275329

276330
} // SUITE(oauth1_tests)
277331

0 commit comments

Comments
 (0)