diff --git a/Release/include/cpprest/details/http_constants.dat b/Release/include/cpprest/details/http_constants.dat index c3b1a53cb..3deb24a14 100644 --- a/Release/include/cpprest/details/http_constants.dat +++ b/Release/include/cpprest/details/http_constants.dat @@ -190,6 +190,7 @@ DAT(expires_in, "expires_in") DAT(grant_type, "grant_type") DAT(redirect_uri, "redirect_uri") DAT(refresh_token, "refresh_token") +DAT(client_credentials, "client_credentials") DAT(response_type, "response_type") DAT(scope, "scope") DAT(state, "state") diff --git a/Release/include/cpprest/oauth2.h b/Release/include/cpprest/oauth2.h index 693ebbe34..68a7c7b9a 100644 --- a/Release/include/cpprest/oauth2.h +++ b/Release/include/cpprest/oauth2.h @@ -284,6 +284,21 @@ public: return _request_token(ub); } + /// + /// Fetches an access token from the token endpoint using client credentials grant type. + /// The task creates an HTTP request to the token_endpoint() using + /// client authentication as the authorization grant. + /// See: http://tools.ietf.org/html/rfc6749#section-4.4 + /// + /// Task that fetches token(s) using client credentials. + pplx::task token_from_client_credentials() + { + uri_builder ub; + ub.append_query( + details::oauth2_strings::grant_type, details::oauth2_strings::client_credentials, false); + return _request_token(ub); + } + /// /// Returns enabled state of the configuration. /// The oauth2_handler will perform OAuth 2.0 authentication only if diff --git a/Release/tests/functional/http/client/oauth2_tests.cpp b/Release/tests/functional/http/client/oauth2_tests.cpp index e1f540858..08bb12a65 100644 --- a/Release/tests/functional/http/client/oauth2_tests.cpp +++ b/Release/tests/functional/http/client/oauth2_tests.cpp @@ -291,6 +291,74 @@ SUITE(oauth2_tests) VERIFY_ARE_EQUAL(U("done"), m_oauth2_config.token().access_token()); } + TEST_FIXTURE(oauth2_test_setup, oauth2_token_from_client_credentials) + { + VERIFY_IS_FALSE(m_oauth2_config.is_enabled()); + + m_oauth2_config.set_user_agent(U("test_user_agent")); + + // Fetch using HTTP Basic authentication. + { + m_scoped.server()->next_request().then([](test_request* request) { + VERIFY_ARE_EQUAL(request->m_method, methods::POST); + + VERIFY_IS_TRUE(is_application_x_www_form_urlencoded(request)); + + VERIFY_ARE_EQUAL( + U("Basic MTIzQUJDOjQ1NkRFRg=="), + request->m_headers[header_names::authorization]); + + VERIFY_ARE_EQUAL( + to_body_data(U("grant_type=client_credentials")), + request->m_body); + + VERIFY_ARE_EQUAL( + U("test_user_agent"), + get_request_user_agent(request)); + + std::map headers; + headers[header_names::content_type] = mime_types::application_json; + request->reply( + status_codes::OK, U(""), headers, "{\"access_token\":\"xyzzy123\",\"token_type\":\"bearer\"}"); + }); + + m_oauth2_config.token_from_client_credentials().wait(); + VERIFY_ARE_EQUAL(U("xyzzy123"), m_oauth2_config.token().access_token()); + VERIFY_IS_TRUE(m_oauth2_config.is_enabled()); + } + + // Fetch using client key & secret in request body (x-www-form-urlencoded). + { + m_scoped.server()->next_request().then([](test_request* request) { + VERIFY_IS_TRUE(is_application_x_www_form_urlencoded(request)); + + VERIFY_ARE_EQUAL(U(""), request->m_headers[header_names::authorization]); + + VERIFY_ARE_EQUAL( + to_body_data(U("grant_type=client_credentials&client_id=123ABC&client_secret=456DEF")), + request->m_body); + + VERIFY_ARE_EQUAL(U("test_user_agent"), get_request_user_agent(request)); + + std::map headers; + headers[header_names::content_type] = mime_types::application_json; + request->reply( + status_codes::OK, U(""), headers, "{\"access_token\":\"xyzzy123\",\"token_type\":\"bearer\"}"); + }); + + m_oauth2_config.set_token(oauth2_token()); // Clear token. + VERIFY_IS_FALSE(m_oauth2_config.is_enabled()); + + m_oauth2_config.set_http_basic_auth(false); + m_oauth2_config.token_from_client_credentials().wait(); + + VERIFY_ARE_EQUAL( + U("xyzzy123"), + m_oauth2_config.token().access_token()); + VERIFY_IS_TRUE(m_oauth2_config.is_enabled()); + } + } + TEST_FIXTURE(oauth2_test_setup, oauth2_bearer_token) { m_oauth2_config.set_token(oauth2_token(U("12345678")));