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")));