diff --git a/Release/include/cpprest/oauth2.h b/Release/include/cpprest/oauth2.h index 202d1520e9..f146990e9b 100644 --- a/Release/include/cpprest/oauth2.h +++ b/Release/include/cpprest/oauth2.h @@ -18,7 +18,8 @@ * * HTTP Library: Oauth 2.0 * -* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk +* For the latest on this and related APIs, please see: +*https://github.com/Microsoft/cpprestsdk * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ @@ -28,516 +29,677 @@ #define _CASA_OAUTH2_H #include "cpprest/http_msg.h" +#include "cpprest/details/web_utilities.h" -namespace web -{ -namespace http -{ -namespace client -{ - // Forward declaration to avoid circular include dependency. - class http_client_config; -} +namespace web { +namespace http { + namespace client { + // Forward declaration to avoid circular include dependency. + class http_client_config; + } -/// oAuth 2.0 library. -namespace oauth2 -{ -namespace details -{ + /// oAuth 2.0 library. + namespace oauth2 { + namespace details { -class oauth2_handler; + class oauth2_handler; -// Constant strings for OAuth 2.0. -typedef utility::string_t oauth2_string; -class oauth2_strings -{ -public: + // Constant strings for OAuth 2.0. + typedef utility::string_t oauth2_string; + class oauth2_strings { + public: #define _OAUTH2_STRINGS #define DAT(a_, b_) _ASYNCRTIMP static const oauth2_string a_; #include "cpprest/details/http_constants.dat" #undef _OAUTH2_STRINGS #undef DAT -}; - -} // namespace web::http::oauth2::details - -/// oAuth functionality is currently in beta. -namespace experimental -{ - -/// -/// Exception type for OAuth 2.0 errors. -/// -class oauth2_exception : public std::exception -{ -public: - oauth2_exception(utility::string_t msg) : m_msg(utility::conversions::to_utf8string(std::move(msg))) {} - ~oauth2_exception() CPPREST_NOEXCEPT {} - const char* what() const CPPREST_NOEXCEPT { return m_msg.c_str(); } - -private: - std::string m_msg; -}; - -/// -/// OAuth 2.0 token and associated information. -/// -class oauth2_token -{ -public: - - /// - /// Value for undefined expiration time in expires_in(). - /// - enum { undefined_expiration = -1 }; - - oauth2_token(utility::string_t access_token=utility::string_t()) : - m_access_token(std::move(access_token)), - m_expires_in(undefined_expiration) - {} - - /// - /// Get access token validity state. - /// If true, access token is a valid. - /// - /// Access token validity state. - bool is_valid_access_token() const { return !access_token().empty(); } - - /// - /// Get access token. - /// - /// Access token string. - const utility::string_t& access_token() const { return m_access_token; } - /// - /// Set access token. - /// - /// Access token string to set. - void set_access_token(utility::string_t access_token) { m_access_token = std::move(access_token); } - - /// - /// Get refresh token. - /// - /// Refresh token string. - const utility::string_t& refresh_token() const { return m_refresh_token; } - /// - /// Set refresh token. - /// - /// Refresh token string to set. - void set_refresh_token(utility::string_t refresh_token) { m_refresh_token = std::move(refresh_token); } - - /// - /// Get token type. - /// - /// Token type string. - const utility::string_t& token_type() const { return m_token_type; } - /// - /// Set token type. - /// - /// Token type string to set. - void set_token_type(utility::string_t token_type) { m_token_type = std::move(token_type); } - - /// - /// Get token scope. - /// - /// Token scope string. - const utility::string_t& scope() const { return m_scope; } - /// - /// Set token scope. - /// - /// Token scope string to set. - void set_scope(utility::string_t scope) { m_scope = std::move(scope); } - - /// - /// Get the lifetime of the access token in seconds. - /// For example, 3600 means the access token will expire in one hour from - /// the time when access token response was generated by the authorization server. - /// Value of undefined_expiration means expiration time is either - /// unset or that it was not returned by the server with the access token. - /// - /// Lifetime of the access token in seconds or undefined_expiration if not set. - int64_t expires_in() const { return m_expires_in; } - /// - /// Set lifetime of access token (in seconds). - /// - /// Lifetime of access token in seconds. - void set_expires_in(int64_t expires_in) { m_expires_in = expires_in; } - -private: - utility::string_t m_access_token; - utility::string_t m_refresh_token; - utility::string_t m_token_type; - utility::string_t m_scope; - int64_t m_expires_in; -}; - -/// -/// OAuth 2.0 configuration. -/// -/// Encapsulates functionality for: -/// - Authenticating requests with an access token. -/// - Performing the OAuth 2.0 authorization code grant authorization flow. -/// See: http://tools.ietf.org/html/rfc6749#section-4.1 -/// - Performing the OAuth 2.0 implicit grant authorization flow. -/// See: http://tools.ietf.org/html/rfc6749#section-4.2 -/// -/// Performing OAuth 2.0 authorization: -/// 1. Set service and client/app parameters: -/// - Client/app key & secret (as provided by the service). -/// - The service authorization endpoint and token endpoint. -/// - Your client/app redirect URI. -/// - Use set_state() to assign a unique state string for the authorization -/// session (default: ""). -/// - If needed, use set_bearer_auth() to control bearer token passing in either -/// query or header (default: header). See: http://tools.ietf.org/html/rfc6750#section-2 -/// - If needed, use set_access_token_key() to set "non-standard" access token -/// key (default: "access_token"). -/// - If needed, use set_implicit_grant() to enable implicit grant flow. -/// 2. Build authorization URI with build_authorization_uri() and open this in web browser/control. -/// 3. The resource owner should then clicks "Yes" to authorize your client/app, and -/// as a result the web browser/control is redirected to redirect_uri(). -/// 5. Capture the redirected URI either in web control or by HTTP listener. -/// 6. Pass the redirected URI to token_from_redirected_uri() to obtain access token. -/// - The method ensures redirected URI contains same state() as set in step 1. -/// - In implicit_grant() is false, this will create HTTP request to fetch access token -/// from the service. Otherwise access token is already included in the redirected URI. -/// -/// Usage for issuing authenticated requests: -/// 1. Perform authorization as above to obtain the access token or use an existing token. -/// - Some services provide option to generate access tokens for testing purposes. -/// 2. Pass the resulting oauth2_config with the access token to http_client_config::set_oauth2(). -/// 3. Construct http_client with this http_client_config. As a result, all HTTP requests -/// by that client will be OAuth 2.0 authenticated. -/// -/// -class oauth2_config -{ -public: - - oauth2_config(utility::string_t client_key, utility::string_t client_secret, - utility::string_t auth_endpoint, utility::string_t token_endpoint, - utility::string_t redirect_uri, utility::string_t scope=utility::string_t()) : - m_client_key(std::move(client_key)), - m_client_secret(std::move(client_secret)), - m_auth_endpoint(std::move(auth_endpoint)), - m_token_endpoint(std::move(token_endpoint)), - m_redirect_uri(std::move(redirect_uri)), - m_scope(std::move(scope)), - m_implicit_grant(false), - m_bearer_auth(true), - m_http_basic_auth(true), - m_access_token_key(details::oauth2_strings::access_token) - {} - - /// - /// Builds an authorization URI to be loaded in the web browser/view. - /// The URI is built with auth_endpoint() as basis. - /// The implicit_grant() affects the built URI by selecting - /// either authorization code or implicit grant flow. - /// You can set generate_state to generate a new random state string. - /// - /// If true, a new random state() string is generated - /// which replaces the current state(). If false, state() is unchanged and used as-is. - /// Authorization URI string. - _ASYNCRTIMP utility::string_t build_authorization_uri(bool generate_state); - - /// - /// Fetch an access token (and possibly a refresh token) based on redirected URI. - /// Behavior depends on the implicit_grant() setting. - /// If implicit_grant() is false, the URI is parsed for 'code' - /// parameter, and then token_from_code() is called with this code. - /// See: http://tools.ietf.org/html/rfc6749#section-4.1 - /// Otherwise, redirect URI fragment part is parsed for 'access_token' - /// parameter, which directly contains the token(s). - /// See: http://tools.ietf.org/html/rfc6749#section-4.2 - /// In both cases, the 'state' parameter is parsed and is verified to match state(). - /// - /// The URI where web browser/view was redirected after resource owner's authorization. - /// Task that fetches the token(s) based on redirected URI. - _ASYNCRTIMP pplx::task token_from_redirected_uri(const web::http::uri& redirected_uri); - - /// - /// Fetches an access token (and possibly a refresh token) from the token endpoint. - /// The task creates an HTTP request to the token_endpoint() which exchanges - /// the authorization code for the token(s). - /// This also sets the refresh token if one was returned. - /// See: http://tools.ietf.org/html/rfc6749#section-4.1.3 - /// - /// Code received via redirect upon successful authorization. - /// Task that fetches token(s) based on the authorization code. - pplx::task token_from_code(utility::string_t authorization_code) - { - uri_builder ub; - ub.append_query(details::oauth2_strings::grant_type, details::oauth2_strings::authorization_code, false); - ub.append_query(details::oauth2_strings::code, uri::encode_data_string(std::move(authorization_code)), false); - ub.append_query(details::oauth2_strings::redirect_uri, uri::encode_data_string(redirect_uri()), false); - return _request_token(ub); - } - - /// - /// Fetches a new access token (and possibly a new refresh token) using the refresh token. - /// The task creates a HTTP request to the token_endpoint(). - /// If successful, resulting access token is set as active via set_token(). - /// See: http://tools.ietf.org/html/rfc6749#section-6 - /// This also sets a new refresh token if one was returned. - /// - /// Task that fetches the token(s) using the refresh token. - pplx::task token_from_refresh() - { - uri_builder ub; - ub.append_query(details::oauth2_strings::grant_type, details::oauth2_strings::refresh_token, false); - ub.append_query(details::oauth2_strings::refresh_token, uri::encode_data_string(token().refresh_token()), false); - return _request_token(ub); - } - - /// - /// Returns enabled state of the configuration. - /// The oauth2_handler will perform OAuth 2.0 authentication only if - /// this method returns true. - /// Return value is true if access token is valid (=fetched or manually set). - /// - /// The configuration enabled state. - bool is_enabled() const { return token().is_valid_access_token(); } - - /// - /// Get client key. - /// - /// Client key string. - const utility::string_t& client_key() const { return m_client_key; } - /// - /// Set client key. - /// - /// Client key string to set. - void set_client_key(utility::string_t client_key) { m_client_key = std::move(client_key); } - - /// - /// Get client secret. - /// - /// Client secret string. - const utility::string_t& client_secret() const { return m_client_secret; } - /// - /// Set client secret. - /// - /// Client secret string to set. - void set_client_secret(utility::string_t client_secret) { m_client_secret = std::move(client_secret); } - - /// - /// Get authorization endpoint URI string. - /// - /// Authorization endpoint URI string. - const utility::string_t& auth_endpoint() const { return m_auth_endpoint; } - /// - /// Set authorization endpoint URI string. - /// - /// Authorization endpoint URI string to set. - void set_auth_endpoint(utility::string_t auth_endpoint) { m_auth_endpoint = std::move(auth_endpoint); } - - /// - /// Get token endpoint URI string. - /// - /// Token endpoint URI string. - const utility::string_t& token_endpoint() const { return m_token_endpoint; } - /// - /// Set token endpoint URI string. - /// - /// Token endpoint URI string to set. - void set_token_endpoint(utility::string_t token_endpoint) { m_token_endpoint = std::move(token_endpoint); } - - /// - /// Get redirect URI string. - /// - /// Redirect URI string. - const utility::string_t& redirect_uri() const { return m_redirect_uri; } - /// - /// Set redirect URI string. - /// - /// Redirect URI string to set. - void set_redirect_uri(utility::string_t redirect_uri) { m_redirect_uri = std::move(redirect_uri); } - - /// - /// Get scope used in authorization for token. - /// - /// Scope string used in authorization. - const utility::string_t& scope() const { return m_scope; } - /// - /// Set scope for authorization for token. - /// - /// Scope string for authorization for token. - void set_scope(utility::string_t scope) { m_scope = std::move(scope); } - - /// - /// Get client state string used in authorization. - /// - /// Client state string used in authorization. - const utility::string_t& state() { return m_state; } - /// - /// Set client state string for authorization for token. - /// The state string is used in authorization for security reasons - /// (to uniquely identify authorization sessions). - /// If desired, suitably secure state string can be automatically generated - /// by build_authorization_uri(). - /// A good state string consist of 30 or more random alphanumeric characters. - /// - /// Client authorization state string to set. - void set_state(utility::string_t state) { m_state = std::move(state); } - - /// - /// Get token. - /// - /// Token. - const oauth2_token& token() const { return m_token; } - /// - /// Set token. - /// - /// Token to set. - void set_token(oauth2_token token) { m_token = std::move(token); } - - /// - /// Get implicit grant setting for authorization. - /// - /// Implicit grant setting for authorization. - bool implicit_grant() const { return m_implicit_grant; } - /// - /// Set implicit grant setting for authorization. - /// False means authorization code grant is used for authorization. - /// True means implicit grant is used. - /// Default: False. - /// - /// The implicit grant setting to set. - void set_implicit_grant(bool implicit_grant) { m_implicit_grant = implicit_grant; } - - /// - /// Get bearer token authentication setting. - /// - /// Bearer token authentication setting. - bool bearer_auth() const { return m_bearer_auth; } - /// - /// Set bearer token authentication setting. - /// This must be selected based on what the service accepts. - /// True means access token is passed in the request header. (http://tools.ietf.org/html/rfc6750#section-2.1) - /// False means access token in passed in the query parameters. (http://tools.ietf.org/html/rfc6750#section-2.3) - /// Default: True. - /// - /// The bearer token authentication setting to set. - void set_bearer_auth(bool bearer_auth) { m_bearer_auth = bearer_auth; } - - /// - /// Get HTTP Basic authentication setting for token endpoint. - /// - /// HTTP Basic authentication setting for token endpoint. - bool http_basic_auth() const { return m_http_basic_auth; } - /// - /// Set HTTP Basic authentication setting for token endpoint. - /// This setting must be selected based on what the service accepts. - /// True means HTTP Basic authentication is used for the token endpoint. - /// False means client key & secret are passed in the HTTP request body. - /// Default: True. - /// - /// The HTTP Basic authentication setting to set. - void set_http_basic_auth(bool http_basic_auth) { m_http_basic_auth = http_basic_auth; } - - /// - /// Get access token key. - /// - /// Access token key string. - const utility::string_t& access_token_key() const { return m_access_token_key; } - /// - /// Set access token key. - /// If the service requires a "non-standard" key you must set it here. - /// Default: "access_token". - /// - void set_access_token_key(utility::string_t access_token_key) { m_access_token_key = std::move(access_token_key); } - - /// - /// Get the web proxy object - /// - /// A reference to the web proxy object. - const web_proxy& proxy() const - { - return m_proxy; - } - - /// - /// Set the web proxy object that will be used by token_from_code and token_from_refresh - /// - /// A reference to the web proxy object. - void set_proxy(const web_proxy& proxy) - { - m_proxy = proxy; - } - -private: - friend class web::http::client::http_client_config; - friend class web::http::oauth2::details::oauth2_handler; - - oauth2_config() : - m_implicit_grant(false), - m_bearer_auth(true), - m_http_basic_auth(true) - {} - - _ASYNCRTIMP pplx::task _request_token(uri_builder& request_body); - - oauth2_token _parse_token_from_json(const json::value& token_json); - - void _authenticate_request(http_request &req) const - { - if (bearer_auth()) - { - req.headers().add(header_names::authorization, _XPLATSTR("Bearer ") + token().access_token()); - } - else - { - uri_builder ub(req.request_uri()); - ub.append_query(access_token_key(), token().access_token()); - req.set_request_uri(ub.to_uri()); - } - } - - utility::string_t m_client_key; - utility::string_t m_client_secret; - utility::string_t m_auth_endpoint; - utility::string_t m_token_endpoint; - utility::string_t m_redirect_uri; - utility::string_t m_scope; - utility::string_t m_state; - - web::web_proxy m_proxy; - - bool m_implicit_grant; - bool m_bearer_auth; - bool m_http_basic_auth; - utility::string_t m_access_token_key; - - oauth2_token m_token; - - utility::nonce_generator m_state_generator; -}; - -} // namespace web::http::oauth2::experimental - -namespace details -{ - -class oauth2_handler : public http_pipeline_stage -{ -public: - oauth2_handler(std::shared_ptr cfg) : - m_config(std::move(cfg)) - {} - - virtual pplx::task propagate(http_request request) override - { - if (m_config) - { - m_config->_authenticate_request(request); + }; + + } // namespace web::http::oauth2::details + + /// oAuth functionality is currently in beta. + namespace experimental { + + /// + /// Exception type for OAuth 2.0 errors. + /// + class oauth2_exception : public std::exception { + public: + oauth2_exception(utility::string_t msg) + : m_msg(utility::conversions::to_utf8string(std::move(msg))) + { + } + ~oauth2_exception() CPPREST_NOEXCEPT {} + const char* what() const CPPREST_NOEXCEPT + { + return m_msg.c_str(); + } + + private: + std::string m_msg; + }; + + /// + /// OAuth 2.0 token and associated information. + /// + class oauth2_token { + public: + /// + /// Value for undefined expiration time in expires_in(). + /// + enum { undefined_expiration = -1 }; + + oauth2_token( + utility::string_t access_token = utility::string_t()) + : m_access_token(std::move(access_token)) + , m_expires_in(undefined_expiration) + { + } + + /// + /// Get access token validity state. + /// If true, access token is a valid. + /// + /// Access token validity state. + bool is_valid_access_token() const + { + return !access_token().empty(); + } + + /// + /// Get access token. + /// + /// Access token string. + const utility::string_t& access_token() const + { + return m_access_token; + } + /// + /// Set access token. + /// + /// Access token string to + /// set. + void set_access_token(utility::string_t access_token) + { + m_access_token = std::move(access_token); + } + + /// + /// Get refresh token. + /// + /// Refresh token string. + const utility::string_t& refresh_token() const + { + return m_refresh_token; + } + /// + /// Set refresh token. + /// + /// Refresh token string to + /// set. + void set_refresh_token(utility::string_t refresh_token) + { + m_refresh_token = std::move(refresh_token); + } + + /// + /// Get token type. + /// + /// Token type string. + const utility::string_t& token_type() const + { + return m_token_type; + } + /// + /// Set token type. + /// + /// Token type string to set. + void set_token_type(utility::string_t token_type) + { + m_token_type = std::move(token_type); + } + + /// + /// Get token scope. + /// + /// Token scope string. + const utility::string_t& scope() const { return m_scope; } + /// + /// Set token scope. + /// + /// Token scope string to set. + void set_scope(utility::string_t scope) + { + m_scope = std::move(scope); + } + + /// + /// Get the lifetime of the access token in seconds. + /// For example, 3600 means the access token will expire in one + /// hour from + /// the time when access token response was generated by the + /// authorization server. + /// Value of undefined_expiration means expiration time is + /// either + /// unset or that it was not returned by the server with the + /// access token. + /// + /// Lifetime of the access token in seconds or + /// undefined_expiration if not set. + int64_t expires_in() const { return m_expires_in; } + /// + /// Set lifetime of access token (in seconds). + /// + /// Lifetime of access token in + /// seconds. + void set_expires_in(int64_t expires_in) + { + m_expires_in = expires_in; + } + + private: + utility::string_t m_access_token; + utility::string_t m_refresh_token; + utility::string_t m_token_type; + utility::string_t m_scope; + int64_t m_expires_in; + }; + + /// + /// OAuth 2.0 configuration. + /// + /// Encapsulates functionality for: + /// - Authenticating requests with an access token. + /// - Performing the OAuth 2.0 authorization code grant + /// authorization flow. + /// See: http://tools.ietf.org/html/rfc6749#section-4.1 + /// - Performing the OAuth 2.0 implicit grant authorization flow. + /// See: http://tools.ietf.org/html/rfc6749#section-4.2 + /// + /// Performing OAuth 2.0 authorization: + /// 1. Set service and client/app parameters: + /// - Client/app key & secret (as provided by the service). + /// - The service authorization endpoint and token endpoint. + /// - Your client/app redirect URI. + /// - Use set_state() to assign a unique state string for the + /// authorization + /// session (default: ""). + /// - If needed, use set_bearer_auth() to control bearer token + /// passing in either + /// query or header (default: header). See: + /// http://tools.ietf.org/html/rfc6750#section-2 + /// - If needed, use set_access_token_key() to set "non-standard" + /// access token + /// key (default: "access_token"). + /// - If needed, use set_implicit_grant() to enable implicit grant + /// flow. + /// 2. Build authorization URI with build_authorization_uri() and + /// open this in web browser/control. + /// 3. The resource owner should then clicks "Yes" to authorize your + /// client/app, and + /// as a result the web browser/control is redirected to + /// redirect_uri(). + /// 5. Capture the redirected URI either in web control or by HTTP + /// listener. + /// 6. Pass the redirected URI to token_from_redirected_uri() to + /// obtain access token. + /// - The method ensures redirected URI contains same state() as + /// set in step 1. + /// - In implicit_grant() is false, this will create HTTP request + /// to fetch access token + /// from the service. Otherwise access token is already included + /// in the redirected URI. + /// + /// Usage for issuing authenticated requests: + /// 1. Perform authorization as above to obtain the access token or + /// use an existing token. + /// - Some services provide option to generate access tokens for + /// testing purposes. + /// 2. Pass the resulting oauth2_config with the access token to + /// http_client_config::set_oauth2(). + /// 3. Construct http_client with this http_client_config. As a + /// result, all HTTP requests + /// by that client will be OAuth 2.0 authenticated. + /// + /// + class oauth2_config { + public: + oauth2_config(utility::string_t client_key, + utility::string_t client_secret, + utility::string_t auth_endpoint, + utility::string_t token_endpoint, + utility::string_t redirect_uri, + utility::string_t scope = utility::string_t()) + : m_client_key(std::move(client_key)) + , m_client_secret(std::move(client_secret)) + , m_auth_endpoint(std::move(auth_endpoint)) + , m_token_endpoint(std::move(token_endpoint)) + , m_redirect_uri(std::move(redirect_uri)) + , m_scope(std::move(scope)) + , m_implicit_grant(false) + , m_bearer_auth(true) + , m_http_basic_auth(true) + , m_access_token_key(details::oauth2_strings::access_token) + { + } + + /// + /// Builds an authorization URI to be loaded in the web + /// browser/view. + /// The URI is built with auth_endpoint() as basis. + /// The implicit_grant() affects the built URI by selecting + /// either authorization code or implicit grant flow. + /// You can set generate_state to generate a new random state + /// string. + /// + /// If true, a new random state() + /// string is generated + /// which replaces the current state(). If false, state() is + /// unchanged and used as-is. + /// Authorization URI string. + _ASYNCRTIMP utility::string_t build_authorization_uri( + bool generate_state); + + /// + /// Fetch an access token (and possibly a refresh token) based + /// on redirected URI. + /// Behavior depends on the implicit_grant() setting. + /// If implicit_grant() is false, the URI is parsed for 'code' + /// parameter, and then token_from_code() is called with this + /// code. + /// See: http://tools.ietf.org/html/rfc6749#section-4.1 + /// Otherwise, redirect URI fragment part is parsed for + /// 'access_token' + /// parameter, which directly contains the token(s). + /// See: http://tools.ietf.org/html/rfc6749#section-4.2 + /// In both cases, the 'state' parameter is parsed and is + /// verified to match state(). + /// + /// The URI where web browser/view + /// was redirected after resource owner's authorization. + /// Task that fetches the token(s) based on redirected + /// URI. + _ASYNCRTIMP pplx::task token_from_redirected_uri( + const web::http::uri& redirected_uri); + + /// + /// Fetches an access token (and possibly a refresh token) from + /// the token endpoint. + /// The task creates an HTTP request to the token_endpoint() + /// which exchanges + /// the authorization code for the token(s). + /// This also sets the refresh token if one was returned. + /// See: http://tools.ietf.org/html/rfc6749#section-4.1.3 + /// + /// Code received via redirect + /// upon successful authorization. + /// Task that fetches token(s) based on the + /// authorization code. + pplx::task token_from_code( + utility::string_t authorization_code) + { + uri_builder ub; + ub.append_query(details::oauth2_strings::grant_type, + details::oauth2_strings::authorization_code, false); + ub.append_query(details::oauth2_strings::code, + uri::encode_data_string(std::move(authorization_code)), + false); + ub.append_query(details::oauth2_strings::redirect_uri, + uri::encode_data_string(redirect_uri()), false); + return _request_token(ub); + } + + /// + /// Fetches a new access token (and possibly a new refresh + /// token) using the refresh token. + /// The task creates a HTTP request to the token_endpoint(). + /// If successful, resulting access token is set as active via + /// set_token(). + /// See: http://tools.ietf.org/html/rfc6749#section-6 + /// This also sets a new refresh token if one was returned. + /// + /// Task that fetches the token(s) using the refresh + /// token. + pplx::task token_from_refresh() + { + uri_builder ub; + ub.append_query(details::oauth2_strings::grant_type, + details::oauth2_strings::refresh_token, false); + ub.append_query(details::oauth2_strings::refresh_token, + uri::encode_data_string(token().refresh_token()), + false); + return _request_token(ub); + } + + /// + /// Returns enabled state of the configuration. + /// The oauth2_handler will perform OAuth 2.0 authentication + /// only if + /// this method returns true. + /// Return value is true if access token is valid (=fetched or + /// manually set). + /// + /// The configuration enabled state. + bool is_enabled() const + { + return token().is_valid_access_token(); + } + + /// + /// Get client key. + /// + /// Client key string. + const utility::string_t& client_key() const + { + return m_client_key; + } + /// + /// Set client key. + /// + /// Client key string to set. + void set_client_key(utility::string_t client_key) + { + m_client_key = std::move(client_key); + } + + /// + /// Get client secret. + /// + /// Client secret string. + const utility::string_t& client_secret() const + { + return m_client_secret; + } + /// + /// Set client secret. + /// + /// Client secret string to + /// set. + void set_client_secret(utility::string_t client_secret) + { + m_client_secret = std::move(client_secret); + } + + /// + /// Get authorization endpoint URI string. + /// + /// Authorization endpoint URI string. + const utility::string_t& auth_endpoint() const + { + return m_auth_endpoint; + } + /// + /// Set authorization endpoint URI string. + /// + /// Authorization endpoint URI + /// string to set. + void set_auth_endpoint(utility::string_t auth_endpoint) + { + m_auth_endpoint = std::move(auth_endpoint); + } + + /// + /// Get token endpoint URI string. + /// + /// Token endpoint URI string. + const utility::string_t& token_endpoint() const + { + return m_token_endpoint; + } + /// + /// Set token endpoint URI string. + /// + /// Token endpoint URI string to + /// set. + void set_token_endpoint(utility::string_t token_endpoint) + { + m_token_endpoint = std::move(token_endpoint); + } + + /// + /// Get redirect URI string. + /// + /// Redirect URI string. + const utility::string_t& redirect_uri() const + { + return m_redirect_uri; + } + /// + /// Set redirect URI string. + /// + /// Redirect URI string to + /// set. + void set_redirect_uri(utility::string_t redirect_uri) + { + m_redirect_uri = std::move(redirect_uri); + } + + /// + /// Get scope used in authorization for token. + /// + /// Scope string used in authorization. + const utility::string_t& scope() const { return m_scope; } + /// + /// Set scope for authorization for token. + /// + /// Scope string for authorization for + /// token. + void set_scope(utility::string_t scope) + { + m_scope = std::move(scope); + } + + /// + /// Get client state string used in authorization. + /// + /// Client state string used in + /// authorization. + const utility::string_t& state() { return m_state; } + /// + /// Set client state string for authorization for token. + /// The state string is used in authorization for security + /// reasons + /// (to uniquely identify authorization sessions). + /// If desired, suitably secure state string can be + /// automatically generated + /// by build_authorization_uri(). + /// A good state string consist of 30 or more random + /// alphanumeric characters. + /// + /// Client authorization state string to + /// set. + void set_state(utility::string_t state) + { + m_state = std::move(state); + } + + /// + /// Get token. + /// + /// Token. + const oauth2_token& token() const { return m_token; } + /// + /// Set token. + /// + /// Token to set. + void set_token(oauth2_token token) + { + m_token = std::move(token); + } + + /// + /// Get implicit grant setting for authorization. + /// + /// Implicit grant setting for authorization. + bool implicit_grant() const { return m_implicit_grant; } + /// + /// Set implicit grant setting for authorization. + /// False means authorization code grant is used for + /// authorization. + /// True means implicit grant is used. + /// Default: False. + /// + /// The implicit grant setting to + /// set. + void set_implicit_grant(bool implicit_grant) + { + m_implicit_grant = implicit_grant; + } + + /// + /// Get bearer token authentication setting. + /// + /// Bearer token authentication setting. + bool bearer_auth() const { return m_bearer_auth; } + /// + /// Set bearer token authentication setting. + /// This must be selected based on what the service accepts. + /// True means access token is passed in the request header. + /// (http://tools.ietf.org/html/rfc6750#section-2.1) + /// False means access token in passed in the query parameters. + /// (http://tools.ietf.org/html/rfc6750#section-2.3) + /// Default: True. + /// + /// The bearer token authentication + /// setting to set. + void set_bearer_auth(bool bearer_auth) + { + m_bearer_auth = bearer_auth; + } + + /// + /// Get HTTP Basic authentication setting for token endpoint. + /// + /// HTTP Basic authentication setting for token + /// endpoint. + bool http_basic_auth() const { return m_http_basic_auth; } + /// + /// Set HTTP Basic authentication setting for token endpoint. + /// This setting must be selected based on what the service + /// accepts. + /// True means HTTP Basic authentication is used for the token + /// endpoint. + /// False means client key & secret are passed in the HTTP + /// request body. + /// Default: True. + /// + /// The HTTP Basic authentication + /// setting to set. + void set_http_basic_auth(bool http_basic_auth) + { + m_http_basic_auth = http_basic_auth; + } + + /// + /// Get access token key. + /// + /// Access token key string. + const utility::string_t& access_token_key() const + { + return m_access_token_key; + } + /// + /// Set access token key. + /// If the service requires a "non-standard" key you must set it + /// here. + /// Default: "access_token". + /// + void set_access_token_key(utility::string_t access_token_key) + { + m_access_token_key = std::move(access_token_key); + } + + /// + /// Get the web proxy object + /// + /// A reference to the web proxy object. + const web_proxy& proxy() const { return m_proxy; } + + /// + /// Set the web proxy object that will be used by + /// token_from_code and token_from_refresh + /// + /// A reference to the web proxy + /// object. + void set_proxy(const web_proxy& proxy) { m_proxy = proxy; } + + private: + friend class web::http::client::http_client_config; + friend class web::http::oauth2::details::oauth2_handler; + + oauth2_config() + : m_implicit_grant(false) + , m_bearer_auth(true) + , m_http_basic_auth(true) + { + } + + _ASYNCRTIMP pplx::task _request_token( + uri_builder& request_body); + + oauth2_token _parse_token_from_json( + const json::value& token_json); + + void _authenticate_request(http_request& req) const + { + if (bearer_auth()) { + req.headers().add(header_names::authorization, + _XPLATSTR("Bearer ") + token().access_token()); + } + else { + uri_builder ub(req.request_uri()); + ub.append_query( + access_token_key(), token().access_token()); + req.set_request_uri(ub.to_uri()); + } + } + + utility::string_t m_client_key; + utility::string_t m_client_secret; + utility::string_t m_auth_endpoint; + utility::string_t m_token_endpoint; + utility::string_t m_redirect_uri; + utility::string_t m_scope; + utility::string_t m_state; + + web::web_proxy m_proxy; + + bool m_implicit_grant; + bool m_bearer_auth; + bool m_http_basic_auth; + utility::string_t m_access_token_key; + + oauth2_token m_token; + + utility::nonce_generator m_state_generator; + }; + + } // namespace web::http::oauth2::experimental + + namespace details { + + class oauth2_handler : public http_pipeline_stage { + public: + oauth2_handler(std::shared_ptr cfg) + : m_config(std::move(cfg)) + { + } + + virtual pplx::task propagate( + http_request request) override + { + if (m_config) { + m_config->_authenticate_request(request); + } + return next_stage()->propagate(request); + } + + private: + std::shared_ptr m_config; + }; } - return next_stage()->propagate(request); } - -private: - std::shared_ptr m_config; -}; - -}}}} +} +} #endif