This is an automated email from the ASF dual-hosted git repository. rxl pushed a commit to branch branch-2.6 in repository https://gitbox.apache.org/repos/asf/pulsar.git
commit 7905aca413f3f16d3e4ffece6a51af4162b23f56 Author: Jia Zhai <zhai...@apache.org> AuthorDate: Wed Jul 8 21:25:19 2020 -0500 Cpp oauth2 auth client (#7467) ### Motivation #7420 provides an Oauth2 auth client for java. This PR tries to support it in cpp client ### Modifications - add implementation - add related tests. (cherry picked from commit 2d0ccebab1dce4c657821fb78a08f4a0bfca2454) --- pulsar-client-cpp/include/pulsar/Authentication.h | 107 +++++++- .../include/pulsar/ClientConfiguration.h | 2 +- pulsar-client-cpp/lib/Authentication.cc | 7 + pulsar-client-cpp/lib/ClientConfiguration.cc | 2 +- pulsar-client-cpp/lib/auth/AuthAthenz.cc | 2 +- pulsar-client-cpp/lib/auth/AuthOauth2.cc | 271 +++++++++++++++++++++ pulsar-client-cpp/lib/auth/AuthOauth2.h | 74 ++++++ pulsar-client-cpp/lib/auth/AuthTls.cc | 2 +- pulsar-client-cpp/lib/auth/AuthToken.cc | 2 +- pulsar-client-cpp/tests/AuthPluginTest.cc | 45 ++++ 10 files changed, 505 insertions(+), 9 deletions(-) diff --git a/pulsar-client-cpp/include/pulsar/Authentication.h b/pulsar-client-cpp/include/pulsar/Authentication.h index efa8d9f..57a3e70 100644 --- a/pulsar-client-cpp/include/pulsar/Authentication.h +++ b/pulsar-client-cpp/include/pulsar/Authentication.h @@ -56,7 +56,7 @@ class PULSAR_PUBLIC Authentication { public: virtual ~Authentication(); virtual const std::string getAuthMethodName() const = 0; - virtual Result getAuthData(AuthenticationDataPtr& authDataContent) const { + virtual Result getAuthData(AuthenticationDataPtr& authDataContent) { authDataContent = authData_; return ResultOk; } @@ -107,7 +107,7 @@ class PULSAR_PUBLIC AuthTls : public Authentication { static AuthenticationPtr create(const std::string& authParamsString); static AuthenticationPtr create(const std::string& certificatePath, const std::string& privateKeyPath); const std::string getAuthMethodName() const; - Result getAuthData(AuthenticationDataPtr& authDataTls) const; + Result getAuthData(AuthenticationDataPtr& authDataTls); private: AuthenticationDataPtr authDataTls_; @@ -144,7 +144,7 @@ class PULSAR_PUBLIC AuthToken : public Authentication { static AuthenticationPtr create(const TokenSupplier& tokenSupplier); const std::string getAuthMethodName() const; - Result getAuthData(AuthenticationDataPtr& authDataToken) const; + Result getAuthData(AuthenticationDataPtr& authDataToken); private: AuthenticationDataPtr authDataToken_; @@ -160,12 +160,111 @@ class PULSAR_PUBLIC AuthAthenz : public Authentication { static AuthenticationPtr create(ParamMap& params); static AuthenticationPtr create(const std::string& authParamsString); const std::string getAuthMethodName() const; - Result getAuthData(AuthenticationDataPtr& authDataAthenz) const; + Result getAuthData(AuthenticationDataPtr& authDataAthenz); private: AuthenticationDataPtr authDataAthenz_; }; +// OAuth 2.0 token and associated information. +// currently mainly works for access token +class Oauth2TokenResult { + public: + enum + { + undefined_expiration = -1 + }; + + Oauth2TokenResult(); + ~Oauth2TokenResult(); + + Oauth2TokenResult& setAccessToken(const std::string& accessToken); + Oauth2TokenResult& setIdToken(const std::string& idToken); + Oauth2TokenResult& setRefreshToken(const std::string& refreshToken); + Oauth2TokenResult& setExpiresIn(const int64_t expiresIn); + + const std::string& getAccessToken() const; + const std::string& getIdToken() const; + const std::string& getRefreshToken() const; + int64_t getExpiresIn() const; + + private: + // map to json "access_token" + std::string accessToken_; + // map to json "id_token" + std::string idToken_; + // map to json "refresh_token" + std::string refreshToken_; + // map to json "expires_in" + int64_t expiresIn_; +}; + +typedef std::shared_ptr<Oauth2TokenResult> Oauth2TokenResultPtr; + +class Oauth2Flow { + public: + virtual ~Oauth2Flow(); + + /** + * Initializes the authorization flow. + */ + virtual void initialize() = 0; + + /** + * Acquires an access token from the OAuth 2.0 authorization server. + * @return a token result including an access token. + */ + virtual Oauth2TokenResultPtr authenticate() = 0; + + /** + * Closes the authorization flow. + */ + virtual void close() = 0; + + protected: + Oauth2Flow(); +}; + +typedef std::shared_ptr<Oauth2Flow> FlowPtr; + +class CachedToken { + public: + ~CachedToken(); + virtual bool isExpired() = 0; + virtual AuthenticationDataPtr getAuthData() = 0; + + protected: + CachedToken(); +}; + +typedef std::shared_ptr<CachedToken> CachedTokenPtr; + +/** + * Oauth2 based implementation of Pulsar client authentication. + * Passed in parameter would be like: + * ``` + * "type": "client_credentials", + * "issuer_url": "https://accounts.google.com", + * "client_id": "d9ZyX97q1ef8Cr81WHVC4hFQ64vSlDK3", + * "client_secret": "on1uJ...k6F6R", + * "audience": "https://broker.example.com" + * ``` + */ +class PULSAR_PUBLIC AuthOauth2 : public Authentication { + public: + AuthOauth2(ParamMap& params); + ~AuthOauth2(); + + static AuthenticationPtr create(ParamMap& params); + static AuthenticationPtr create(const std::string& authParamsString); + const std::string getAuthMethodName() const; + Result getAuthData(AuthenticationDataPtr& authDataOauth2); + + private: + FlowPtr flowPtr_; + CachedTokenPtr cachedTokenPtr_; +}; + } // namespace pulsar #endif /* PULSAR_AUTHENTICATION_H_ */ diff --git a/pulsar-client-cpp/include/pulsar/ClientConfiguration.h b/pulsar-client-cpp/include/pulsar/ClientConfiguration.h index e65aecc..9bb63d4 100644 --- a/pulsar-client-cpp/include/pulsar/ClientConfiguration.h +++ b/pulsar-client-cpp/include/pulsar/ClientConfiguration.h @@ -43,7 +43,7 @@ class PULSAR_PUBLIC ClientConfiguration { /** * @return the authentication data */ - const Authentication& getAuth() const; + Authentication& getAuth() const; /** * Set timeout on client operations (subscribe, create producer, close, unsubscribe) diff --git a/pulsar-client-cpp/lib/Authentication.cc b/pulsar-client-cpp/lib/Authentication.cc index f552b53..105d1c3 100644 --- a/pulsar-client-cpp/lib/Authentication.cc +++ b/pulsar-client-cpp/lib/Authentication.cc @@ -22,6 +22,7 @@ #include "auth/AuthTls.h" #include "auth/AuthAthenz.h" #include "auth/AuthToken.h" +#include "auth/AuthOauth2.h" #include <lib/LogUtils.h> #include <string> @@ -125,6 +126,9 @@ AuthenticationPtr tryCreateBuiltinAuth(const std::string& pluginName, ParamMap& } else if (boost::iequals(pluginName, ATHENZ_PLUGIN_NAME) || boost::iequals(pluginName, ATHENZ_JAVA_PLUGIN_NAME)) { return AuthAthenz::create(paramMap); + } else if (boost::iequals(pluginName, OAUTH2_TOKEN_PLUGIN_NAME) || + boost::iequals(pluginName, OAUTH2_TOKEN_JAVA_PLUGIN_NAME)) { + return AuthOauth2::create(paramMap); } else { return AuthenticationPtr(); } @@ -139,6 +143,9 @@ AuthenticationPtr tryCreateBuiltinAuth(const std::string& pluginName, const std: } else if (boost::iequals(pluginName, ATHENZ_PLUGIN_NAME) || boost::iequals(pluginName, ATHENZ_JAVA_PLUGIN_NAME)) { return AuthAthenz::create(authParamsString); + } else if (boost::iequals(pluginName, OAUTH2_TOKEN_PLUGIN_NAME) || + boost::iequals(pluginName, OAUTH2_TOKEN_JAVA_PLUGIN_NAME)) { + return AuthOauth2::create(authParamsString); } else { return AuthenticationPtr(); } diff --git a/pulsar-client-cpp/lib/ClientConfiguration.cc b/pulsar-client-cpp/lib/ClientConfiguration.cc index 1733dde..ad210f6 100644 --- a/pulsar-client-cpp/lib/ClientConfiguration.cc +++ b/pulsar-client-cpp/lib/ClientConfiguration.cc @@ -36,7 +36,7 @@ ClientConfiguration& ClientConfiguration::setAuth(const AuthenticationPtr& authe return *this; } -const Authentication& ClientConfiguration::getAuth() const { return *impl_->authenticationPtr; } +Authentication& ClientConfiguration::getAuth() const { return *impl_->authenticationPtr; } const AuthenticationPtr& ClientConfiguration::getAuthPtr() const { return impl_->authenticationPtr; } diff --git a/pulsar-client-cpp/lib/auth/AuthAthenz.cc b/pulsar-client-cpp/lib/auth/AuthAthenz.cc index 1e6c8fe..3141fb3 100644 --- a/pulsar-client-cpp/lib/auth/AuthAthenz.cc +++ b/pulsar-client-cpp/lib/auth/AuthAthenz.cc @@ -91,7 +91,7 @@ AuthenticationPtr AuthAthenz::create(ParamMap& params) { const std::string AuthAthenz::getAuthMethodName() const { return "athenz"; } -Result AuthAthenz::getAuthData(AuthenticationDataPtr& authDataContent) const { +Result AuthAthenz::getAuthData(AuthenticationDataPtr& authDataContent) { authDataContent = authDataAthenz_; return ResultOk; } diff --git a/pulsar-client-cpp/lib/auth/AuthOauth2.cc b/pulsar-client-cpp/lib/auth/AuthOauth2.cc new file mode 100644 index 0000000..3104c4d --- /dev/null +++ b/pulsar-client-cpp/lib/auth/AuthOauth2.cc @@ -0,0 +1,271 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#include <lib/auth/AuthOauth2.h> + +#include <curl/curl.h> +#include <sstream> +#include <boost/property_tree/json_parser.hpp> +#include <boost/property_tree/ptree.hpp> +#include <boost/date_time/posix_time/posix_time.hpp> + +#include <lib/LogUtils.h> +DECLARE_LOG_OBJECT() + +namespace pulsar { + +// AuthDataOauth2 + +AuthDataOauth2::AuthDataOauth2(const std::string& accessToken) { accessToken_ = accessToken; } + +AuthDataOauth2::~AuthDataOauth2() {} + +bool AuthDataOauth2::hasDataForHttp() { return true; } + +std::string AuthDataOauth2::getHttpHeaders() { return "Authorization: Bearer " + accessToken_; } + +bool AuthDataOauth2::hasDataFromCommand() { return true; } + +std::string AuthDataOauth2::getCommandData() { return accessToken_; } + +// Oauth2TokenResult + +Oauth2TokenResult::Oauth2TokenResult() { expiresIn_ = undefined_expiration; } + +Oauth2TokenResult::~Oauth2TokenResult() {} + +Oauth2TokenResult& Oauth2TokenResult::setAccessToken(const std::string& accessToken) { + accessToken_ = accessToken; + return *this; +} + +Oauth2TokenResult& Oauth2TokenResult::setIdToken(const std::string& idToken) { + idToken_ = idToken; + return *this; +} + +Oauth2TokenResult& Oauth2TokenResult::setRefreshToken(const std::string& refreshToken) { + refreshToken_ = refreshToken; + return *this; +} + +Oauth2TokenResult& Oauth2TokenResult::setExpiresIn(const int64_t expiresIn) { + expiresIn_ = expiresIn; + return *this; +} + +const std::string& Oauth2TokenResult::getAccessToken() const { return accessToken_; } + +const std::string& Oauth2TokenResult::getIdToken() const { return idToken_; } + +const std::string& Oauth2TokenResult::getRefreshToken() const { return refreshToken_; } + +int64_t Oauth2TokenResult::getExpiresIn() const { return expiresIn_; } + +// CachedToken + +CachedToken::CachedToken() {} + +CachedToken::~CachedToken() {} + +// Oauth2CachedToken + +static int64_t currentTimeMillis() { + using namespace boost::posix_time; + using boost::posix_time::milliseconds; + using boost::posix_time::seconds; + static ptime time_t_epoch(boost::gregorian::date(1970, 1, 1)); + + time_duration diff = microsec_clock::universal_time() - time_t_epoch; + return diff.total_milliseconds(); +} + +Oauth2CachedToken::Oauth2CachedToken(Oauth2TokenResultPtr token) { + latest_ = token; + + int64_t expiredIn = token->getExpiresIn(); + if (expiredIn > 0) { + expiresAt_ = expiredIn + currentTimeMillis(); + } else { + throw "ExpiresIn in Oauth2TokenResult invalid value: " + expiredIn; + } + authData_ = AuthenticationDataPtr(new AuthDataOauth2(token->getAccessToken())); +} + +AuthenticationDataPtr Oauth2CachedToken::getAuthData() { return authData_; } + +Oauth2CachedToken::~Oauth2CachedToken() {} + +bool Oauth2CachedToken::isExpired() { return expiresAt_ < currentTimeMillis(); } + +// OauthFlow + +Oauth2Flow::Oauth2Flow() {} +Oauth2Flow::~Oauth2Flow() {} + +// ClientCredentialFlow + +ClientCredentialFlow::ClientCredentialFlow(const std::string& issuerUrl, const std::string& clientId, + const std::string& clientSecret, const std::string& audience) { + issuerUrl_ = issuerUrl; + clientId_ = clientId; + clientSecret_ = clientSecret; + audience_ = audience; +} + +void ClientCredentialFlow::initialize() {} +void ClientCredentialFlow::close() {} + +static size_t curlWriteCallback(void* contents, size_t size, size_t nmemb, void* responseDataPtr) { + ((std::string*)responseDataPtr)->append((char*)contents, size * nmemb); + return size * nmemb; +} + +Oauth2TokenResultPtr ClientCredentialFlow::authenticate() { + Oauth2TokenResultPtr resultPtr = Oauth2TokenResultPtr(new Oauth2TokenResult()); + + CURL* handle = curl_easy_init(); + CURLcode res; + std::string responseData; + + // set header: json, request type: post + struct curl_slist* list = NULL; + list = curl_slist_append(list, "Content-Type: application/json"); + curl_easy_setopt(handle, CURLOPT_HTTPHEADER, list); + curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "POST"); + + // set URL: issuerUrl + curl_easy_setopt(handle, CURLOPT_URL, issuerUrl_.c_str()); + + // Write callback + curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, curlWriteCallback); + curl_easy_setopt(handle, CURLOPT_WRITEDATA, &responseData); + + // New connection is made for each call + curl_easy_setopt(handle, CURLOPT_FRESH_CONNECT, 1L); + curl_easy_setopt(handle, CURLOPT_FORBID_REUSE, 1L); + + curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0L); + curl_easy_setopt(handle, CURLOPT_SSL_VERIFYHOST, 0L); + + // fill in the request data + boost::property_tree::ptree pt; + pt.put("grant_type", "client_credentials"); + pt.put("client_id", clientId_); + pt.put("client_secret", clientSecret_); + pt.put("audience", audience_); + + std::stringstream ss; + boost::property_tree::json_parser::write_json(ss, pt); + std::string ssString = ss.str(); + + curl_easy_setopt(handle, CURLOPT_POSTFIELDS, ssString.c_str()); + + // Make get call to server + res = curl_easy_perform(handle); + + LOG_DEBUG("issuerUrl_ " << issuerUrl_ << " clientid: " << clientId_ << " client_secret " << clientSecret_ + << " audience " << audience_ << " ssstring " << ssString); + + switch (res) { + case CURLE_OK: + long response_code; + curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &response_code); + LOG_DEBUG("Response received for issuerurl " << issuerUrl_ << " code " << response_code); + if (response_code == 200) { + boost::property_tree::ptree root; + std::stringstream stream; + stream << responseData; + try { + boost::property_tree::read_json(stream, root); + } catch (boost::property_tree::json_parser_error& e) { + LOG_ERROR("Failed to parse json of Oauth2 response: " + << e.what() << "\nInput Json = " << responseData << " passedin: " << ssString); + break; + } + + resultPtr->setAccessToken(root.get<std::string>("access_token")); + resultPtr->setExpiresIn(root.get<uint32_t>("expires_in")); + + LOG_DEBUG("access_token: " << resultPtr->getAccessToken() + << " expires_in: " << resultPtr->getExpiresIn()); + } else { + LOG_ERROR("Response failed for issuerurl " << issuerUrl_ << ". response Code " + << response_code << " passedin: " << ssString); + } + break; + default: + LOG_ERROR("Response failed for issuerurl " << issuerUrl_ << ". Error Code " << res + << " passedin: " << ssString); + break; + } + // Free header list + curl_slist_free_all(list); + curl_easy_cleanup(handle); + + return resultPtr; +} + +// AuthOauth2 + +AuthOauth2::AuthOauth2(ParamMap& params) { + flowPtr_ = FlowPtr(new ClientCredentialFlow(params["issuer_url"], params["client_id"], + params["client_secret"], params["audience"])); +} + +AuthOauth2::~AuthOauth2() {} + +ParamMap parseJsonAuthParamsString(const std::string& authParamsString) { + ParamMap params; + if (!authParamsString.empty()) { + boost::property_tree::ptree root; + std::stringstream stream; + stream << authParamsString; + try { + boost::property_tree::read_json(stream, root); + for (const auto& item : root) { + params[item.first] = item.second.get_value<std::string>(); + } + } catch (boost::property_tree::json_parser_error& e) { + LOG_ERROR("Invalid String Error: " << e.what()); + } + } + return params; +} + +AuthenticationPtr AuthOauth2::create(const std::string& authParamsString) { + ParamMap params = parseJsonAuthParamsString(authParamsString); + + return create(params); +} + +AuthenticationPtr AuthOauth2::create(ParamMap& params) { return AuthenticationPtr(new AuthOauth2(params)); } + +const std::string AuthOauth2::getAuthMethodName() const { return "token"; } + +Result AuthOauth2::getAuthData(AuthenticationDataPtr& authDataContent) { + if (cachedTokenPtr_ == nullptr || cachedTokenPtr_->isExpired()) { + cachedTokenPtr_ = CachedTokenPtr(new Oauth2CachedToken(flowPtr_->authenticate())); + } + + authDataContent = cachedTokenPtr_->getAuthData(); + return ResultOk; +} + +} // namespace pulsar diff --git a/pulsar-client-cpp/lib/auth/AuthOauth2.h b/pulsar-client-cpp/lib/auth/AuthOauth2.h new file mode 100644 index 0000000..0090976 --- /dev/null +++ b/pulsar-client-cpp/lib/auth/AuthOauth2.h @@ -0,0 +1,74 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#pragma once + +#include <pulsar/Authentication.h> +#include <string> +#include <boost/function.hpp> + +namespace pulsar { + +const std::string OAUTH2_TOKEN_PLUGIN_NAME = "oauth2token"; +const std::string OAUTH2_TOKEN_JAVA_PLUGIN_NAME = + "org.apache.pulsar.client.impl.auth.oauth2.AuthenticationOAuth2"; + +class ClientCredentialFlow : public Oauth2Flow { + public: + ClientCredentialFlow(const std::string& issuerUrl, const std::string& clientId, + const std::string& clientSecret, const std::string& audience); + void initialize(); + Oauth2TokenResultPtr authenticate(); + void close(); + + private: + std::string issuerUrl_; + std::string clientId_; + std::string clientSecret_; + std::string audience_; +}; + +class Oauth2CachedToken : public CachedToken { + public: + Oauth2CachedToken(Oauth2TokenResultPtr token); + ~Oauth2CachedToken(); + bool isExpired(); + AuthenticationDataPtr getAuthData(); + + private: + int64_t expiresAt_; + Oauth2TokenResultPtr latest_; + AuthenticationDataPtr authData_; +}; + +class AuthDataOauth2 : public AuthenticationDataProvider { + public: + AuthDataOauth2(const std::string& accessToken); + ~AuthDataOauth2(); + + bool hasDataForHttp(); + std::string getHttpHeaders(); + bool hasDataFromCommand(); + std::string getCommandData(); + + private: + std::string accessToken_; +}; + +} // namespace pulsar diff --git a/pulsar-client-cpp/lib/auth/AuthTls.cc b/pulsar-client-cpp/lib/auth/AuthTls.cc index fcf6571..fdf7f21 100644 --- a/pulsar-client-cpp/lib/auth/AuthTls.cc +++ b/pulsar-client-cpp/lib/auth/AuthTls.cc @@ -53,7 +53,7 @@ AuthenticationPtr AuthTls::create(const std::string& certificatePath, const std: const std::string AuthTls::getAuthMethodName() const { return "tls"; } -Result AuthTls::getAuthData(AuthenticationDataPtr& authDataContent) const { +Result AuthTls::getAuthData(AuthenticationDataPtr& authDataContent) { authDataContent = authDataTls_; return ResultOk; } diff --git a/pulsar-client-cpp/lib/auth/AuthToken.cc b/pulsar-client-cpp/lib/auth/AuthToken.cc index e377139..429f409 100644 --- a/pulsar-client-cpp/lib/auth/AuthToken.cc +++ b/pulsar-client-cpp/lib/auth/AuthToken.cc @@ -109,7 +109,7 @@ AuthenticationPtr AuthToken::create(const TokenSupplier &tokenSupplier) { const std::string AuthToken::getAuthMethodName() const { return "token"; } -Result AuthToken::getAuthData(AuthenticationDataPtr &authDataContent) const { +Result AuthToken::getAuthData(AuthenticationDataPtr &authDataContent) { authDataContent = authDataToken_; return ResultOk; } diff --git a/pulsar-client-cpp/tests/AuthPluginTest.cc b/pulsar-client-cpp/tests/AuthPluginTest.cc index a447a3a..183c880 100644 --- a/pulsar-client-cpp/tests/AuthPluginTest.cc +++ b/pulsar-client-cpp/tests/AuthPluginTest.cc @@ -336,3 +336,48 @@ TEST(AuthPluginTest, testAuthFactoryAthenz) { } } } + +TEST(AuthPluginTest, testOauth2) { + // test success get token from oauth2 server. + pulsar::AuthenticationDataPtr data; + std::string params = R"({ + "type": "client_credentials", + "issuer_url": "https://dev-kt-aa9ne.us.auth0.com/oauth/token", + "client_id": "Xd23RHsUnvUlP7wchjNYOaIfazgeHd9x", + "client_secret": "rT7ps7WY8uhdVuBTKWZkttwLdQotmdEliaM5rLfmgNibvqziZ-g07ZH52N_poGAb", + "audience": "https://dev-kt-aa9ne.us.auth0.com/api/v2/"})"; + + int expectedTokenLength = 3379; + LOG_INFO("PARAMS: " << params); + pulsar::AuthenticationPtr auth = pulsar::AuthOauth2::create(params); + ASSERT_EQ(auth->getAuthMethodName(), "token"); + ASSERT_EQ(auth->getAuthData(data), pulsar::ResultOk); + ASSERT_EQ(data->hasDataForHttp(), true); + ASSERT_EQ(data->hasDataFromCommand(), true); + ASSERT_EQ(data->getCommandData().length(), expectedTokenLength); +} + +TEST(AuthPluginTest, testOauth2WrongSecret) { + try { + pulsar::AuthenticationDataPtr data; + + std::string params = R"({ + "type": "client_credentials", + "issuer_url": "https://dev-kt-aa9ne.us.auth0.com/oauth/token", + "client_id": "Xd23RHsUnvUlP7wchjNYOaIfazgeHd9x", + "client_secret": "rT7ps7WY8uhdVuBTKWZkttwLdQotmdEliaM5rLfmgNibvqziZ", + "audience": "https://dev-kt-aa9ne.us.auth0.com/api/v2/"})"; + + int expectedTokenLength = 3379; + LOG_INFO("PARAMS: " << params); + pulsar::AuthenticationPtr auth = pulsar::AuthOauth2::create(params); + ASSERT_EQ(auth->getAuthMethodName(), "token"); + + auth->getAuthData(data); + + FAIL() << "Expected fail for wrong secret when to get token from server"; + + } catch (...) { + // expected + } +}