diff --git a/olp-cpp-sdk-authentication/src/AuthenticationClientImpl.cpp b/olp-cpp-sdk-authentication/src/AuthenticationClientImpl.cpp index 77dd3055d..3c269c4d9 100644 --- a/olp-cpp-sdk-authentication/src/AuthenticationClientImpl.cpp +++ b/olp-cpp-sdk-authentication/src/AuthenticationClientImpl.cpp @@ -32,7 +32,6 @@ #include #include "AuthenticationClientUtils.h" #include "Constants.h" -#include "Crypto.h" #include "SignInResultImpl.h" #include "SignInUserResultImpl.h" #include "SignOutResultImpl.h" @@ -43,31 +42,14 @@ #include "olp/core/http/NetworkUtils.h" #include "olp/core/logging/Log.h" #include "olp/core/thread/TaskScheduler.h" -#include "olp/core/utils/Base64.h" -#include "olp/core/utils/Url.h" namespace { -namespace auth = olp::authentication; namespace client = olp::client; using olp::authentication::Constants; -// Helper characters -constexpr auto kParamAdd = "&"; -constexpr auto kParamComma = ","; -constexpr auto kParamEquals = "="; -constexpr auto kParamQuote = "\""; -constexpr auto kLineFeed = '\n'; - // Tags constexpr auto kApplicationJson = "application/json"; -const std::string kOauthPost = "POST"; -const std::string kOauthConsumerKey = "oauth_consumer_key"; -const std::string kOauthNonce = "oauth_nonce"; -const std::string kOauthSignature = "oauth_signature"; -const std::string kOauthTimestamp = "oauth_timestamp"; -const std::string kOauthVersion = "oauth_version"; -const std::string kOauthSignatureMethod = "oauth_signature_method"; const std::string kOauthEndpoint = "/oauth2/token"; const std::string kSignoutEndpoint = "/logout"; const std::string kTermsEndpoint = "/terms"; @@ -106,8 +88,6 @@ constexpr auto kResource = "resource"; constexpr auto kDiagnostics = "diagnostics"; constexpr auto kOperator = "operator"; // Values -constexpr auto kVersion = "1.0"; -constexpr auto kHmac = "HMAC-SHA256"; constexpr auto kErrorWrongTimestamp = 401204; constexpr auto kLogTag = "AuthenticationClient"; @@ -135,9 +115,11 @@ olp::client::HttpResponse AuthenticationClientImpl::CallAuth( const SignInProperties& properties, std::time_t timestamp) { const auto url = settings_.token_endpoint_url + kOauthEndpoint; + auto auth_header = + GenerateAuthorizationHeader(credentials, url, timestamp, GenerateUid()); + client::OlpClient::ParametersType headers = { - {http::kAuthorizationHeader, - GenerateHeader(credentials, url, timestamp)}}; + {http::kAuthorizationHeader, std::move(auth_header)}}; if (context.IsCancelled()) { return {static_cast(olp::http::ErrorCode::CANCELLED_ERROR), "Cancelled"}; @@ -354,8 +336,11 @@ client::CancellationToken AuthenticationClientImpl::HandleUserRequest( network_settings.WithProxySettings(settings_.network_proxy_settings.get()); } request.WithVerb(http::NetworkRequest::HttpVerb::POST); - request.WithHeader(http::kAuthorizationHeader, - GenerateHeader(credentials, url)); + + auto auth_header = GenerateAuthorizationHeader( + credentials, url, std::time(nullptr), GenerateUid()); + + request.WithHeader(http::kAuthorizationHeader, std::move(auth_header)); request.WithHeader(http::kContentTypeHeader, kApplicationJson); request.WithHeader(http::kUserAgentHeader, http::kOlpSdkUserAgent); request.WithSettings(std::move(network_settings)); @@ -465,8 +450,11 @@ client::CancellationToken AuthenticationClientImpl::SignUpHereUser( network_settings.WithProxySettings(settings_.network_proxy_settings.get()); } request.WithVerb(http::NetworkRequest::HttpVerb::POST); - request.WithHeader(http::kAuthorizationHeader, - GenerateHeader(credentials, url)); + + auto auth_header = GenerateAuthorizationHeader( + credentials, url, std::time(nullptr), GenerateUid()); + + request.WithHeader(http::kAuthorizationHeader, std::move(auth_header)); request.WithHeader(http::kContentTypeHeader, kApplicationJson); request.WithHeader(http::kUserAgentHeader, http::kOlpSdkUserAgent); request.WithSettings(std::move(network_settings)); @@ -695,47 +683,6 @@ client::CancellationToken AuthenticationClientImpl::Authorize( std::move(callback)); } -std::string AuthenticationClientImpl::Base64Encode( - const std::vector& vector) { - std::string ret = olp::utils::Base64Encode(vector); - // Base64 encode sometimes return multiline with garbage at the end - if (!ret.empty()) { - auto loc = ret.find(kLineFeed); - if (loc != std::string::npos) ret = ret.substr(0, loc); - } - return ret; -} - -std::string AuthenticationClientImpl::GenerateHeader( - const AuthenticationCredentials& credentials, const std::string& url, - const time_t& timestamp) { - std::string uid = GenerateUid(); - const std::string currentTime = std::to_string(timestamp); - const std::string encodedUri = utils::Url::Encode(url); - const std::string encodedQuery = utils::Url::Encode( - kOauthConsumerKey + kParamEquals + credentials.GetKey() + kParamAdd + - kOauthNonce + kParamEquals + uid + kParamAdd + kOauthSignatureMethod + - kParamEquals + kHmac + kParamAdd + kOauthTimestamp + kParamEquals + - currentTime + kParamAdd + kOauthVersion + kParamEquals + kVersion); - const std::string signatureBase = - kOauthPost + kParamAdd + encodedUri + kParamAdd + encodedQuery; - const std::string encodeKey = credentials.GetSecret() + kParamAdd; - auto hmacResult = Crypto::hmac_sha256(encodeKey, signatureBase); - auto signature = Base64Encode(hmacResult); - std::string authorization = - "OAuth " + kOauthConsumerKey + kParamEquals + kParamQuote + - utils::Url::Encode(credentials.GetKey()) + kParamQuote + kParamComma + - kOauthNonce + kParamEquals + kParamQuote + utils::Url::Encode(uid) + - kParamQuote + kParamComma + kOauthSignatureMethod + kParamEquals + - kParamQuote + kHmac + kParamQuote + kParamComma + kOauthTimestamp + - kParamEquals + kParamQuote + utils::Url::Encode(currentTime) + - kParamQuote + kParamComma + kOauthVersion + kParamEquals + kParamQuote + - kVersion + kParamQuote + kParamComma + kOauthSignature + kParamEquals + - kParamQuote + utils::Url::Encode(signature) + kParamQuote; - - return authorization; -} - std::string AuthenticationClientImpl::GenerateBearerHeader( const std::string& bearer_token) { std::string authorization = http::kBearer + std::string(" "); diff --git a/olp-cpp-sdk-authentication/src/AuthenticationClientImpl.h b/olp-cpp-sdk-authentication/src/AuthenticationClientImpl.h index a14661947..e5a1f7df4 100644 --- a/olp-cpp-sdk-authentication/src/AuthenticationClientImpl.h +++ b/olp-cpp-sdk-authentication/src/AuthenticationClientImpl.h @@ -126,12 +126,6 @@ class AuthenticationClientImpl final { static TimeResponse ParseTimeResponse(std::stringstream& payload); - std::string Base64Encode(const std::vector& vector); - - std::string GenerateHeader(const AuthenticationCredentials& credentials, - const std::string& url, - const time_t& timestamp = std::time(nullptr)); - std::string GenerateBearerHeader(const std::string& bearer_token); client::OlpClient::RequestBodyType GenerateClientBody( diff --git a/olp-cpp-sdk-authentication/src/AuthenticationClientUtils.cpp b/olp-cpp-sdk-authentication/src/AuthenticationClientUtils.cpp index 8367c80a9..e2904aeb7 100644 --- a/olp-cpp-sdk-authentication/src/AuthenticationClientUtils.cpp +++ b/olp-cpp-sdk-authentication/src/AuthenticationClientUtils.cpp @@ -19,21 +19,54 @@ #include "AuthenticationClientUtils.h" +#include +#include +#include + #include #include #include #include -#include -#include -#include - #include "Constants.h" +#include "Crypto.h" #include "olp/core/http/NetworkUtils.h" +#include "olp/core/utils/Base64.h" +#include "olp/core/utils/Url.h" namespace olp { namespace authentication { -namespace auth = olp::authentication; + +namespace { +// Helper characters +constexpr auto kParamAdd = "&"; +constexpr auto kParamComma = ","; +constexpr auto kParamEquals = "="; +constexpr auto kParamQuote = "\""; +constexpr auto kLineFeed = '\n'; + +constexpr auto kOauthPost = "POST"; +constexpr auto kOauthVersion = "oauth_version"; +constexpr auto kOauthConsumerKey = "oauth_consumer_key"; +constexpr auto kOauthNonce = "oauth_nonce"; +constexpr auto kOauthSignature = "oauth_signature"; +constexpr auto kOauthTimestamp = "oauth_timestamp"; +constexpr auto kOauthSignatureMethod = "oauth_signature_method"; +constexpr auto kVersion = "1.0"; +constexpr auto kHmac = "HMAC-SHA256"; + +std::string Base64Encode(const std::vector& vector) { + std::string ret = olp::utils::Base64Encode(vector); + // Base64 encode sometimes return multiline with garbage at the end + if (!ret.empty()) { + auto loc = ret.find(kLineFeed); + if (loc != std::string::npos) ret = ret.substr(0, loc); + } + return ret; +} + +} // namespace + namespace client = olp::client; constexpr auto kDate = "date"; @@ -75,9 +108,8 @@ void ExecuteOrSchedule( task_scheduler->ScheduleTask(std::move(func)); } -auth::IntrospectAppResult GetIntrospectAppResult( - const rapidjson::Document& doc) { - auth::IntrospectAppResult result; +IntrospectAppResult GetIntrospectAppResult(const rapidjson::Document& doc) { + IntrospectAppResult result; if (doc.HasMember(Constants::CLIENT_ID)) { result.SetClientId(doc[Constants::CLIENT_ID].GetString()); } @@ -161,27 +193,27 @@ auth::IntrospectAppResult GetIntrospectAppResult( return result; } -auth::DecisionType GetPermission(const std::string& str) { - return (str.compare("allow") == 0) ? auth::DecisionType::kAllow - : auth::DecisionType::kDeny; +DecisionType GetPermission(const std::string& str) { + return (str.compare("allow") == 0) ? DecisionType::kAllow + : DecisionType::kDeny; } -std::vector GetDiagnostics(rapidjson::Document& doc) { - std::vector results; +std::vector GetDiagnostics(rapidjson::Document& doc) { + std::vector results; const auto& array = doc[Constants::DIAGNOSTICS].GetArray(); for (auto& element : array) { - auth::ActionResult action; + ActionResult action; if (element.HasMember(Constants::DECISION)) { action.SetDecision( GetPermission(element[Constants::DECISION].GetString())); // get permissions if avialible if (element.HasMember(Constants::PERMISSIONS) && element[Constants::PERMISSIONS].IsArray()) { - std::vector permissions; + std::vector permissions; const auto& permissions_array = element[Constants::PERMISSIONS].GetArray(); for (auto& permission_element : permissions_array) { - auth::ActionResult::Permissions permission; + ActionResult::Permissions permission; if (permission_element.HasMember(Constants::ACTION)) { permission.first = permission_element[Constants::ACTION].GetString(); @@ -201,8 +233,8 @@ std::vector GetDiagnostics(rapidjson::Document& doc) { return results; } -auth::AuthorizeResult GetAuthorizeResult(rapidjson::Document& doc) { - auth::AuthorizeResult result; +AuthorizeResult GetAuthorizeResult(rapidjson::Document& doc) { + AuthorizeResult result; if (doc.HasMember(Constants::IDENTITY)) { auto uris = doc[Constants::IDENTITY].GetObject(); @@ -227,7 +259,7 @@ auth::AuthorizeResult GetAuthorizeResult(rapidjson::Document& doc) { } client::OlpClient CreateOlpClient( - const auth::AuthenticationSettings& auth_settings, + const AuthenticationSettings& auth_settings, boost::optional authentication_settings) { client::OlpClientSettings settings; settings.network_request_handler = auth_settings.network_request_handler; @@ -242,5 +274,48 @@ client::OlpClient CreateOlpClient( return client; } +std::string GenerateAuthorizationHeader( + const AuthenticationCredentials& credentials, const std::string& url, + time_t timestamp, std::string nonce) { + const std::string timestamp_str = std::to_string(timestamp); + + std::stringstream stream; + + stream << kOauthConsumerKey << kParamEquals << credentials.GetKey() + << kParamAdd << kOauthNonce << kParamEquals << nonce << kParamAdd + << kOauthSignatureMethod << kParamEquals << kHmac << kParamAdd + << kOauthTimestamp << kParamEquals << timestamp_str << kParamAdd + << kOauthVersion << kParamEquals << kVersion; + + const auto encoded_query = utils::Url::Encode(stream.str()); + + stream.clear(); + + stream << kOauthPost << kParamAdd << utils::Url::Encode(url) << kParamAdd + << encoded_query; + + const auto signature_base = stream.str(); + + stream.clear(); + + const std::string encode_key = credentials.GetSecret() + kParamAdd; + auto hmac_result = Crypto::hmac_sha256(encode_key, signature_base); + auto signature = Base64Encode(hmac_result); + + stream << "OAuth " << kOauthConsumerKey << kParamEquals << kParamQuote + << utils::Url::Encode(credentials.GetKey()) << kParamQuote + << kParamComma << kOauthNonce << kParamEquals << kParamQuote + << utils::Url::Encode(nonce) << kParamQuote << kParamComma + << kOauthSignatureMethod << kParamEquals << kParamQuote << kHmac + << kParamQuote << kParamComma << kOauthTimestamp << kParamEquals + << kParamQuote << utils::Url::Encode(timestamp_str) << kParamQuote + << kParamComma << kOauthVersion << kParamEquals << kParamQuote + << kVersion << kParamQuote << kParamComma << kOauthSignature + << kParamEquals << kParamQuote << utils::Url::Encode(signature) + << kParamQuote; + + return stream.str(); +} + } // namespace authentication } // namespace olp diff --git a/olp-cpp-sdk-authentication/src/AuthenticationClientUtils.h b/olp-cpp-sdk-authentication/src/AuthenticationClientUtils.h index 843af53ea..da1a1929c 100644 --- a/olp-cpp-sdk-authentication/src/AuthenticationClientUtils.h +++ b/olp-cpp-sdk-authentication/src/AuthenticationClientUtils.h @@ -25,6 +25,7 @@ #include +#include "olp/authentication/AuthenticationCredentials.h" #include "olp/authentication/AuthenticationSettings.h" #include "olp/authentication/AuthorizeResult.h" #include "olp/authentication/ErrorResponse.h" @@ -129,5 +130,19 @@ client::OlpClient CreateOlpClient( const AuthenticationSettings& auth_settings, boost::optional authentication_settings); +/* + * @brief Generate authorization header. + * + * @param credentials Client credentials. + * @param url Authorization endpoint URL. + * @param timestamp Current time. + * @param nonce A unique value, must be used once. (Refer to OAuth docs). + * + * @return The authorization header string. + */ +std::string GenerateAuthorizationHeader( + const AuthenticationCredentials& credentials, const std::string& url, + time_t timestamp, std::string nonce); + } // namespace authentication } // namespace olp diff --git a/olp-cpp-sdk-authentication/tests/AuthenticationClientTest.cpp b/olp-cpp-sdk-authentication/tests/AuthenticationClientTest.cpp index ff2658a74..5eaa8ba99 100644 --- a/olp-cpp-sdk-authentication/tests/AuthenticationClientTest.cpp +++ b/olp-cpp-sdk-authentication/tests/AuthenticationClientTest.cpp @@ -28,10 +28,27 @@ namespace { constexpr auto kTime = "Fri, 29 May 2020 11:07:45 GMT"; } // namespace +namespace auth = olp::authentication; + TEST(AuthenticationClientTest, TimeParsing) { { SCOPED_TRACE("Parse time"); - - EXPECT_EQ(olp::authentication::ParseTime(kTime), 1590750465); + EXPECT_EQ(auth::ParseTime(kTime), 1590750465); } } + +TEST(AuthenticationClientTest, GenerateAuthorizationHeader) { + auth::AuthenticationCredentials credentials("key", "secret"); + const auto url = "https://auth.server.com"; + auto sig = auth::GenerateAuthorizationHeader(credentials, url, 0, "unique"); + auto expected_sig = + "oauth_consumer_key=key&oauth_nonce=unique&oauth_signature_method=HMAC-" + "SHA256&oauth_timestamp=0&oauth_version=1.0POST&https%3A%2F%2Fauth." + "server.com&oauth_consumer_key%3Dkey%26oauth_nonce%3Dunique%26oauth_" + "signature_method%3DHMAC-SHA256%26oauth_timestamp%3D0%26oauth_version%" + "3D1.0OAuth " + "oauth_consumer_key=\"key\",oauth_nonce=\"unique\",oauth_signature_" + "method=\"HMAC-SHA256\",oauth_timestamp=\"0\",oauth_version=\"1.0\"," + "oauth_signature=\"ncwRtcqRSM04FIFch8Ay4l7bRmp96lifuHEops4AqEw%3D\""; + EXPECT_EQ(sig, expected_sig); +}