1 /*** 2 * Copyright (C) Microsoft. All rights reserved. 3 * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. 4 * 5 * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ 6 * 7 * HTTP Library: Oauth 1.0 8 * 9 * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk 10 * 11 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 12 ****/ 13 #pragma once 14 15 #ifndef CASA_OAUTH1_H 16 #define CASA_OAUTH1_H 17 18 #include "cpprest/details/web_utilities.h" 19 #include "cpprest/http_msg.h" 20 21 namespace web 22 { 23 namespace http 24 { 25 namespace client 26 { 27 // Forward declaration to avoid circular include dependency. 28 class http_client_config; 29 } // namespace client 30 31 /// oAuth 1.0 library. 32 namespace oauth1 33 { 34 namespace details 35 { 36 class oauth1_handler; 37 38 // State currently used by oauth1_config to authenticate request. 39 // The state varies for every request (due to timestamp and nonce). 40 // The state also contains extra transmitted protocol parameters during 41 // authorization flow (i.e. 'oauth_callback' or 'oauth_verifier'). 42 class oauth1_state 43 { 44 public: 45 oauth1_state(utility::string_t timestamp, 46 utility::string_t nonce, 47 utility::string_t extra_key = utility::string_t(), 48 utility::string_t extra_value = utility::string_t()) m_timestamp(std::move (timestamp))49 : m_timestamp(std::move(timestamp)) 50 , m_nonce(std::move(nonce)) 51 , m_extra_key(std::move(extra_key)) 52 , m_extra_value(std::move(extra_value)) 53 { 54 } 55 timestamp()56 const utility::string_t& timestamp() const { return m_timestamp; } set_timestamp(utility::string_t timestamp)57 void set_timestamp(utility::string_t timestamp) { m_timestamp = std::move(timestamp); } 58 nonce()59 const utility::string_t& nonce() const { return m_nonce; } set_nonce(utility::string_t nonce)60 void set_nonce(utility::string_t nonce) { m_nonce = std::move(nonce); } 61 extra_key()62 const utility::string_t& extra_key() const { return m_extra_key; } set_extra_key(utility::string_t key)63 void set_extra_key(utility::string_t key) { m_extra_key = std::move(key); } 64 extra_value()65 const utility::string_t& extra_value() const { return m_extra_value; } set_extra_value(utility::string_t value)66 void set_extra_value(utility::string_t value) { m_extra_value = std::move(value); } 67 68 private: 69 utility::string_t m_timestamp; 70 utility::string_t m_nonce; 71 utility::string_t m_extra_key; 72 utility::string_t m_extra_value; 73 }; 74 75 // Constant strings for OAuth 1.0. 76 typedef utility::string_t oauth1_string; 77 class oauth1_strings 78 { 79 public: 80 #define _OAUTH1_STRINGS 81 #define DAT(a_, b_) _ASYNCRTIMP static const oauth1_string a_; 82 #include "cpprest/details/http_constants.dat" 83 #undef _OAUTH1_STRINGS 84 #undef DAT 85 }; 86 87 } // namespace details 88 89 /// oAuth functionality is currently in beta. 90 namespace experimental 91 { 92 /// <summary> 93 /// Constant strings for OAuth 1.0 signature methods. 94 /// </summary> 95 typedef utility::string_t oauth1_method; 96 class oauth1_methods 97 { 98 public: 99 #define _OAUTH1_METHODS 100 #define DAT(a, b) _ASYNCRTIMP static const oauth1_method a; 101 #include "cpprest/details/http_constants.dat" 102 #undef _OAUTH1_METHODS 103 #undef DAT 104 }; 105 106 /// <summary> 107 /// Exception type for OAuth 1.0 errors. 108 /// </summary> 109 class oauth1_exception : public std::exception 110 { 111 public: oauth1_exception(utility::string_t msg)112 oauth1_exception(utility::string_t msg) : m_msg(utility::conversions::to_utf8string(std::move(msg))) {} ~oauth1_exception()113 ~oauth1_exception() CPPREST_NOEXCEPT {} what()114 const char* what() const CPPREST_NOEXCEPT { return m_msg.c_str(); } 115 116 private: 117 std::string m_msg; 118 }; 119 120 /// <summary> 121 /// OAuth 1.0 token and associated information. 122 /// </summary> 123 class oauth1_token 124 { 125 public: 126 /// <summary> 127 /// Constructs an initially empty invalid access token. 128 /// </summary> oauth1_token()129 oauth1_token() {} 130 131 /// <summary> 132 /// Constructs a OAuth1 token from a given access token and secret. 133 /// </summary> 134 /// <param name="access_token">Access token string.</param> 135 /// <param name="secret">Token secret string.</param> oauth1_token(utility::string_t access_token,utility::string_t secret)136 oauth1_token(utility::string_t access_token, utility::string_t secret) 137 : m_token(std::move(access_token)), m_secret(std::move(secret)) 138 { 139 } 140 141 /// <summary> 142 /// Get access token validity state. 143 /// If true, token is a valid access token. 144 /// </summary> 145 /// <returns>Access token validity state of the token.</returns> is_valid_access_token()146 bool is_valid_access_token() const { return !(access_token().empty() || secret().empty()); } 147 148 /// <summary> 149 /// Get access token. 150 /// </summary> 151 /// <returns>The access token string.</returns> access_token()152 const utility::string_t& access_token() const { return m_token; } 153 154 /// <summary> 155 /// Set access token. 156 /// </summary> 157 /// <param name="access_token">Access token string to set.</param> set_access_token(utility::string_t && access_token)158 void set_access_token(utility::string_t&& access_token) { m_token = std::move(access_token); } 159 160 /// <summary> 161 /// Set access token. 162 /// </summary> 163 /// <param name="access_token">Access token string to set.</param> set_access_token(const utility::string_t & access_token)164 void set_access_token(const utility::string_t& access_token) { m_token = access_token; } 165 166 /// <summary> 167 /// Get token secret. 168 /// </summary> 169 /// <returns>Token secret string.</returns> secret()170 const utility::string_t& secret() const { return m_secret; } 171 172 /// <summary> 173 /// Set token secret. 174 /// </summary> 175 /// <param name="secret">Token secret string to set.</param> set_secret(utility::string_t && secret)176 void set_secret(utility::string_t&& secret) { m_secret = std::move(secret); } 177 178 /// <summary> 179 /// Set token secret. 180 /// </summary> 181 /// <param name="secret">Token secret string to set.</param> set_secret(const utility::string_t & secret)182 void set_secret(const utility::string_t& secret) { m_secret = secret; } 183 184 /// <summary> 185 /// Retrieves any additional parameters. 186 /// </summary> 187 /// <returns>A map containing the additional parameters.</returns> additional_parameters()188 const std::map<utility::string_t, utility::string_t>& additional_parameters() const 189 { 190 return m_additional_parameters; 191 } 192 193 /// <summary> 194 /// Sets a specific parameter additional parameter. 195 /// </summary> 196 /// <param name="paramName">Parameter name.</param> 197 /// <param name="paramValue">Parameter value.</param> set_additional_parameter(utility::string_t && paramName,utility::string_t && paramValue)198 void set_additional_parameter(utility::string_t&& paramName, utility::string_t&& paramValue) 199 { 200 m_additional_parameters[std::move(paramName)] = std::move(paramValue); 201 } 202 203 /// <summary> 204 /// Sets a specific parameter additional parameter. 205 /// </summary> 206 /// <param name="paramName">Parameter name.</param> 207 /// <param name="paramValue">Parameter value.</param> set_additional_parameter(const utility::string_t & paramName,const utility::string_t & paramValue)208 void set_additional_parameter(const utility::string_t& paramName, const utility::string_t& paramValue) 209 { 210 m_additional_parameters[paramName] = paramValue; 211 } 212 213 /// <summary> 214 /// Clears all additional parameters. 215 /// </summary> clear_additional_parameters()216 void clear_additional_parameters() { m_additional_parameters.clear(); } 217 218 private: 219 friend class oauth1_config; 220 221 utility::string_t m_token; 222 utility::string_t m_secret; 223 std::map<utility::string_t, utility::string_t> m_additional_parameters; 224 }; 225 226 /// <summary> 227 /// OAuth 1.0 configuration class. 228 /// </summary> 229 class oauth1_config 230 { 231 public: 232 oauth1_config(utility::string_t consumer_key, 233 utility::string_t consumer_secret, 234 utility::string_t temp_endpoint, 235 utility::string_t auth_endpoint, 236 utility::string_t token_endpoint, 237 utility::string_t callback_uri, 238 oauth1_method method, 239 utility::string_t realm = utility::string_t()) m_consumer_key(std::move (consumer_key))240 : m_consumer_key(std::move(consumer_key)) 241 , m_consumer_secret(std::move(consumer_secret)) 242 , m_temp_endpoint(std::move(temp_endpoint)) 243 , m_auth_endpoint(std::move(auth_endpoint)) 244 , m_token_endpoint(std::move(token_endpoint)) 245 , m_callback_uri(std::move(callback_uri)) 246 , m_realm(std::move(realm)) 247 , m_method(std::move(method)) 248 , m_is_authorization_completed(false) 249 { 250 } 251 252 /// <summary> 253 /// Builds an authorization URI to be loaded in a web browser/view. 254 /// The URI is built with auth_endpoint() as basis. 255 /// The method creates a task for HTTP request to first obtain a 256 /// temporary token. The authorization URI build based on this token. 257 /// </summary> 258 /// <returns>Authorization URI to be loaded in a web browser/view.</returns> 259 _ASYNCRTIMP pplx::task<utility::string_t> build_authorization_uri(); 260 261 /// <summary> 262 /// Fetch an access token based on redirected URI. 263 /// The URI is expected to contain 'oauth_verifier' 264 /// parameter, which is then used to fetch an access token using the 265 /// token_from_verifier() method. 266 /// See: http://tools.ietf.org/html/rfc5849#section-2.2 267 /// The received 'oauth_token' is parsed and verified to match the current token(). 268 /// When access token is successfully obtained, set_token() is called, and config is 269 /// ready for use by oauth1_handler. 270 /// </summary> 271 /// <param name="redirected_uri">The URI where web browser/view was redirected after resource owner's 272 /// authorization.</param> <returns>Task that fetches the access token based on redirected URI.</returns> 273 _ASYNCRTIMP pplx::task<void> token_from_redirected_uri(const web::http::uri& redirected_uri); 274 275 /// <summary> 276 /// Creates a task with HTTP request to fetch an access token from the token endpoint. 277 /// The request exchanges a verifier code to an access token. 278 /// If successful, the resulting token is set as active via set_token(). 279 /// See: http://tools.ietf.org/html/rfc5849#section-2.3 280 /// </summary> 281 /// <param name="verifier">Verifier received via redirect upon successful authorization.</param> 282 /// <returns>Task that fetches the access token based on the verifier.</returns> token_from_verifier(utility::string_t verifier)283 pplx::task<void> token_from_verifier(utility::string_t verifier) 284 { 285 return _request_token(_generate_auth_state(details::oauth1_strings::verifier, std::move(verifier)), false); 286 } 287 288 /// <summary> 289 /// Creates a task with HTTP request to fetch an access token from the token endpoint. 290 /// If successful, the resulting token is set as active via set_token(). 291 /// </summary> 292 /// <returns>Task that fetches the access token based on the verifier.</returns> refresh_token(const utility::string_t & key)293 pplx::task<void> refresh_token(const utility::string_t& key) 294 { 295 return _request_token(_generate_auth_state(key, m_token.additional_parameters().at(key)), false); 296 } 297 298 /// <summary> 299 /// Get consumer key used in authorization and authentication. 300 /// </summary> 301 /// <returns>Consumer key string.</returns> consumer_key()302 const utility::string_t& consumer_key() const { return m_consumer_key; } 303 /// <summary> 304 /// Set consumer key used in authorization and authentication. 305 /// </summary> 306 /// <param name="key">Consumer key string to set.</param> set_consumer_key(utility::string_t key)307 void set_consumer_key(utility::string_t key) { m_consumer_key = std::move(key); } 308 309 /// <summary> 310 /// Get consumer secret used in authorization and authentication. 311 /// </summary> 312 /// <returns>Consumer secret string.</returns> consumer_secret()313 const utility::string_t& consumer_secret() const { return m_consumer_secret; } 314 /// <summary> 315 /// Set consumer secret used in authorization and authentication. 316 /// </summary> 317 /// <param name="secret">Consumer secret string to set.</param> set_consumer_secret(utility::string_t secret)318 void set_consumer_secret(utility::string_t secret) { m_consumer_secret = std::move(secret); } 319 320 /// <summary> 321 /// Get temporary token endpoint URI string. 322 /// </summary> 323 /// <returns>Temporary token endpoint URI string.</returns> temp_endpoint()324 const utility::string_t& temp_endpoint() const { return m_temp_endpoint; } 325 /// <summary> 326 /// Set temporary token endpoint URI string. 327 /// </summary> 328 /// <param name="temp_endpoint">Temporary token endpoint URI string to set.</param> set_temp_endpoint(utility::string_t temp_endpoint)329 void set_temp_endpoint(utility::string_t temp_endpoint) { m_temp_endpoint = std::move(temp_endpoint); } 330 331 /// <summary> 332 /// Get authorization endpoint URI string. 333 /// </summary> 334 /// <returns>Authorization endpoint URI string.</returns> auth_endpoint()335 const utility::string_t& auth_endpoint() const { return m_auth_endpoint; } 336 /// <summary> 337 /// Set authorization endpoint URI string. 338 /// </summary> 339 /// <param name="auth_endpoint">Authorization endpoint URI string to set.</param> set_auth_endpoint(utility::string_t auth_endpoint)340 void set_auth_endpoint(utility::string_t auth_endpoint) { m_auth_endpoint = std::move(auth_endpoint); } 341 342 /// <summary> 343 /// Get token endpoint URI string. 344 /// </summary> 345 /// <returns>Token endpoint URI string.</returns> token_endpoint()346 const utility::string_t& token_endpoint() const { return m_token_endpoint; } 347 /// <summary> 348 /// Set token endpoint URI string. 349 /// </summary> 350 /// <param name="token_endpoint">Token endpoint URI string to set.</param> set_token_endpoint(utility::string_t token_endpoint)351 void set_token_endpoint(utility::string_t token_endpoint) { m_token_endpoint = std::move(token_endpoint); } 352 353 /// <summary> 354 /// Get callback URI string. 355 /// </summary> 356 /// <returns>Callback URI string.</returns> callback_uri()357 const utility::string_t& callback_uri() const { return m_callback_uri; } 358 /// <summary> 359 /// Set callback URI string. 360 /// </summary> 361 /// <param name="callback_uri">Callback URI string to set.</param> set_callback_uri(utility::string_t callback_uri)362 void set_callback_uri(utility::string_t callback_uri) { m_callback_uri = std::move(callback_uri); } 363 364 /// <summary> 365 /// Get token. 366 /// </summary> 367 /// <returns>Token.</returns> 368 _ASYNCRTIMP const oauth1_token& token() const; 369 370 /// <summary> 371 /// Set token. 372 /// </summary> 373 /// <param name="token">Token to set.</param> set_token(oauth1_token token)374 void set_token(oauth1_token token) 375 { 376 m_token = std::move(token); 377 m_is_authorization_completed = true; 378 } 379 380 /// <summary> 381 /// Get signature method. 382 /// </summary> 383 /// <returns>Signature method.</returns> method()384 const oauth1_method& method() const { return m_method; } 385 /// <summary> 386 /// Set signature method. 387 /// </summary> 388 /// <param name="method">Signature method.</param> set_method(oauth1_method method)389 void set_method(oauth1_method method) { m_method = std::move(method); } 390 391 /// <summary> 392 /// Get authentication realm. 393 /// </summary> 394 /// <returns>Authentication realm string.</returns> realm()395 const utility::string_t& realm() const { return m_realm; } 396 /// <summary> 397 /// Set authentication realm. 398 /// </summary> 399 /// <param name="realm">Authentication realm string to set.</param> set_realm(utility::string_t realm)400 void set_realm(utility::string_t realm) { m_realm = std::move(realm); } 401 402 /// <summary> 403 /// Returns enabled state of the configuration. 404 /// The oauth1_handler will perform OAuth 1.0 authentication only if 405 /// this method returns true. 406 /// Return value is true if access token is valid (=fetched or manually set) 407 /// and both consumer_key() and consumer_secret() are set (=non-empty). 408 /// </summary> 409 /// <returns>The configuration enabled state.</returns> is_enabled()410 bool is_enabled() const 411 { 412 return token().is_valid_access_token() && !(consumer_key().empty() || consumer_secret().empty()); 413 } 414 415 // Builds signature base string according to: 416 // http://tools.ietf.org/html/rfc5849#section-3.4.1.1 417 _ASYNCRTIMP utility::string_t _build_signature_base_string(http_request request, details::oauth1_state state) const; 418 419 // Builds HMAC-SHA1 signature according to: 420 // http://tools.ietf.org/html/rfc5849#section-3.4.2 _build_hmac_sha1_signature(http_request request,details::oauth1_state state)421 utility::string_t _build_hmac_sha1_signature(http_request request, details::oauth1_state state) const 422 { 423 auto text(_build_signature_base_string(std::move(request), std::move(state))); 424 auto digest(_hmac_sha1(_build_key(), std::move(text))); 425 auto signature(utility::conversions::to_base64(std::move(digest))); 426 return signature; 427 } 428 429 // Builds PLAINTEXT signature according to: 430 // http://tools.ietf.org/html/rfc5849#section-3.4.4 _build_plaintext_signature()431 utility::string_t _build_plaintext_signature() const { return _build_key(); } 432 _generate_auth_state(utility::string_t extra_key,utility::string_t extra_value)433 details::oauth1_state _generate_auth_state(utility::string_t extra_key, utility::string_t extra_value) 434 { 435 return details::oauth1_state( 436 _generate_timestamp(), _generate_nonce(), std::move(extra_key), std::move(extra_value)); 437 } 438 _generate_auth_state()439 details::oauth1_state _generate_auth_state() 440 { 441 return details::oauth1_state(_generate_timestamp(), _generate_nonce()); 442 } 443 444 /// <summary> 445 /// Gets map of parameters to sign. 446 /// </summary> 447 /// <returns>Map of parameters.</returns> parameters()448 const std::map<utility::string_t, utility::string_t>& parameters() const { return m_parameters_to_sign; } 449 450 /// <summary> 451 /// Adds a key value parameter. 452 /// </summary> 453 /// <param name="key">Key as a string value.</param> 454 /// <param name="value">Value as a string value.</param> add_parameter(const utility::string_t & key,const utility::string_t & value)455 void add_parameter(const utility::string_t& key, const utility::string_t& value) 456 { 457 m_parameters_to_sign[key] = value; 458 } 459 460 /// <summary> 461 /// Adds a key value parameter. 462 /// </summary> 463 /// <param name="key">Key as a string value.</param> 464 /// <param name="value">Value as a string value.</param> add_parameter(utility::string_t && key,utility::string_t && value)465 void add_parameter(utility::string_t&& key, utility::string_t&& value) 466 { 467 m_parameters_to_sign[std::move(key)] = std::move(value); 468 } 469 470 /// <summary> 471 /// Sets entire map or parameters replacing all previously values. 472 /// </summary> 473 /// <param name="parameters">Map of values.</param> set_parameters(const std::map<utility::string_t,utility::string_t> & parameters)474 void set_parameters(const std::map<utility::string_t, utility::string_t>& parameters) 475 { 476 m_parameters_to_sign.clear(); 477 m_parameters_to_sign = parameters; 478 } 479 480 /// <summary> 481 /// Clears all parameters. 482 /// </summary> clear_parameters()483 void clear_parameters() { m_parameters_to_sign.clear(); } 484 485 /// <summary> 486 /// Get the web proxy object 487 /// </summary> 488 /// <returns>A reference to the web proxy object.</returns> proxy()489 const web_proxy& proxy() const { return m_proxy; } 490 491 /// <summary> 492 /// Set the web proxy object that will be used by token_from_code and token_from_refresh 493 /// </summary> 494 /// <param name="proxy">A reference to the web proxy object.</param> set_proxy(const web_proxy & proxy)495 void set_proxy(const web_proxy& proxy) { m_proxy = proxy; } 496 497 private: 498 friend class web::http::client::http_client_config; 499 friend class web::http::oauth1::details::oauth1_handler; 500 oauth1_config()501 oauth1_config() : m_is_authorization_completed(false) {} 502 _generate_nonce()503 utility::string_t _generate_nonce() { return m_nonce_generator.generate(); } 504 _generate_timestamp()505 static utility::string_t _generate_timestamp() 506 { 507 return utility::conversions::details::to_string_t(utility::datetime::utc_timestamp()); 508 } 509 510 _ASYNCRTIMP static std::vector<unsigned char> __cdecl _hmac_sha1(const utility::string_t& key, 511 const utility::string_t& data); 512 513 static utility::string_t _build_base_string_uri(const uri& u); 514 515 utility::string_t _build_normalized_parameters(web::http::uri u, const details::oauth1_state& state) const; 516 517 utility::string_t _build_signature(http_request request, details::oauth1_state state) const; 518 _build_key()519 utility::string_t _build_key() const 520 { 521 return uri::encode_data_string(consumer_secret()) + _XPLATSTR("&") + uri::encode_data_string(m_token.secret()); 522 } 523 _authenticate_request(http_request & req)524 void _authenticate_request(http_request& req) { _authenticate_request(req, _generate_auth_state()); } 525 526 _ASYNCRTIMP void _authenticate_request(http_request& req, details::oauth1_state state); 527 528 _ASYNCRTIMP pplx::task<void> _request_token(details::oauth1_state state, bool is_temp_token_request); 529 530 utility::string_t m_consumer_key; 531 utility::string_t m_consumer_secret; 532 oauth1_token m_token; 533 534 utility::string_t m_temp_endpoint; 535 utility::string_t m_auth_endpoint; 536 utility::string_t m_token_endpoint; 537 utility::string_t m_callback_uri; 538 utility::string_t m_realm; 539 oauth1_method m_method; 540 541 std::map<utility::string_t, utility::string_t> m_parameters_to_sign; 542 543 web::web_proxy m_proxy; 544 545 utility::nonce_generator m_nonce_generator; 546 bool m_is_authorization_completed; 547 }; 548 549 } // namespace experimental 550 551 namespace details 552 { 553 class oauth1_handler : public http_pipeline_stage 554 { 555 public: oauth1_handler(std::shared_ptr<experimental::oauth1_config> cfg)556 oauth1_handler(std::shared_ptr<experimental::oauth1_config> cfg) : m_config(std::move(cfg)) {} 557 propagate(http_request request)558 virtual pplx::task<http_response> propagate(http_request request) override 559 { 560 if (m_config) 561 { 562 m_config->_authenticate_request(request); 563 } 564 return next_stage()->propagate(request); 565 } 566 567 private: 568 std::shared_ptr<experimental::oauth1_config> m_config; 569 }; 570 571 } // namespace details 572 } // namespace oauth1 573 } // namespace http 574 } // namespace web 575 576 #endif 577