Skip to content

Commit

Permalink
Added scope support to authentication
Browse files Browse the repository at this point in the history
* Added new method AuthenticationClient::SignInClient() which takes
  new SignInProperties structure as a parameter.
* Old AuthenticationClient::SignInClient method deprecated.
* Added integration test to cover new scope case.

Resolves: OLPEDGE-874

Signed-off-by: Serhii Lozynskyi <ext-serhii.lozynskyi@here.com>
  • Loading branch information
meravingen123 committed Dec 10, 2019
1 parent 8354621 commit 9790f56
Show file tree
Hide file tree
Showing 8 changed files with 153 additions and 66 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

#pragma once

#include <boost/optional.hpp>
#include <chrono>
#include <functional>
#include <memory>
Expand Down Expand Up @@ -65,6 +66,23 @@ static const std::string kHereAccountProductionUrl =
*/
class AUTHENTICATION_API AuthenticationClient {
public:
/**
* @brief General properties used to sign in using client credentials.
*/
struct SignInProperties {
/**
* @brief scope Optional. Scope that should be assigned to access token.
*/
boost::optional<std::string> scope{boost::none};

/**
* @brief expires_in Optional. Number of seconds before token expires, must
* number zero or more. Ignored if zero or greater than default expiration
* of the application.
*/
std::chrono::seconds expires_in{0};
};

/**
* @brief The password user sign-in properties struct.
*/
Expand Down Expand Up @@ -284,11 +302,32 @@ class AUTHENTICATION_API AuthenticationClient {
* application.
* @return CancellationToken that can be used to cancel the request.
*/
OLP_SDK_DEPRECATED(
"Deprecated. Please use version with properties parameter. Will be "
"removed by 03.2020")
client::CancellationToken SignInClient(
const AuthenticationCredentials& credentials,
const SignInClientCallback& callback,
const std::chrono::seconds& expires_in = std::chrono::seconds(0));

/**
* @brief Sign in with HERE account client credentials, requests a client
* access token that identifies the application or service by providing client
* credentials. Client access tokens can not be refreshed, instead request a
* new one using client credentials.
* @param credentials Client access keys issued for the client by the HERE
* Account as part of the onboarding or support process.
* @param properties SignInProperties structure that has scope and expire
* properties.
* @param callback The method to be called when request is completed. In case
* of successful client sign-in request, the returned HTTP status is 200.
* Otherwise, check the response error.
* @return CancellationToken that can be used to cancel the request.
*/
client::CancellationToken SignInClient(AuthenticationCredentials credentials,
SignInProperties properties,
SignInClientCallback callback);

/**
* @brief Sign in by providing the user email and password previously
* registered using the sign-up API, requests a user access token. User access
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,12 @@ class AUTHENTICATION_API SignInResult {
*/
const std::string& GetUserIdentifier() const;

/**
* @brief Scope that is assigned to access token.
* @return String containing the scope.
*/
const std::string& GetScope() const;

private:
friend class SignInUserResult;
friend class AuthenticationClient;
Expand Down
37 changes: 24 additions & 13 deletions olp-cpp-sdk-authentication/src/AuthenticationClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ constexpr auto kDateOfBirth = "dob";
constexpr auto kEmail = "email";
constexpr auto kFirstName = "firstname";
constexpr auto kGrantType = "grantType";
constexpr auto kScope = "scope";
constexpr auto kInviteToken = "inviteToken";
constexpr auto kLanguage = "language";
constexpr auto kLastName = "lastname";
Expand Down Expand Up @@ -165,9 +166,8 @@ class AuthenticationClient::Impl final {
* @return CancellationToken that can be used to cancel the request.
*/
client::CancellationToken SignInClient(
const AuthenticationCredentials& credentials,
const std::chrono::seconds& expires_in,
const AuthenticationClient::SignInClientCallback& callback);
AuthenticationCredentials credentials, SignInProperties properties,
AuthenticationClient::SignInClientCallback callback);

client::CancellationToken SignInHereUser(
const AuthenticationCredentials& credentials,
Expand Down Expand Up @@ -212,7 +212,7 @@ class AuthenticationClient::Impl final {
std::string generateBearerHeader(const std::string& bearer_token);

http::NetworkRequest::RequestBodyType generateClientBody(
unsigned int expires_in);
const SignInProperties& properties);
http::NetworkRequest::RequestBodyType generateUserBody(
const AuthenticationClient::UserProperties& properties);
http::NetworkRequest::RequestBodyType generateFederatedBody(
Expand Down Expand Up @@ -279,9 +279,8 @@ AuthenticationClient::Impl::Impl(const std::string& authentication_server_url,
network_settings_() {}

client::CancellationToken AuthenticationClient::Impl::SignInClient(
const AuthenticationCredentials& credentials,
const std::chrono::seconds& expires_in,
const AuthenticationClient::SignInClientCallback& callback) {
AuthenticationCredentials credentials, SignInProperties properties,
AuthenticationClient::SignInClientCallback callback) {
if (!network_) {
ExecuteOrSchedule(task_scheduler_, [callback] {
AuthenticationError result({static_cast<int>(http::ErrorCode::IO_ERROR),
Expand All @@ -303,8 +302,7 @@ client::CancellationToken AuthenticationClient::Impl::SignInClient(

std::shared_ptr<std::stringstream> payload =
std::make_shared<std::stringstream>();
request.WithBody(
generateClientBody(static_cast<unsigned int>(expires_in.count())));
request.WithBody(generateClientBody(properties));
auto cache = client_token_cache_;
auto send_outcome = network_->Send(
request, payload,
Expand Down Expand Up @@ -724,19 +722,25 @@ std::string AuthenticationClient::Impl::generateBearerHeader(
}

http::NetworkRequest::RequestBodyType
AuthenticationClient::Impl::generateClientBody(unsigned int expires_in) {
AuthenticationClient::Impl::generateClientBody(
const SignInProperties& properties) {
rapidjson::StringBuffer data;
rapidjson::Writer<rapidjson::StringBuffer> writer(data);
writer.StartObject();

writer.Key(kGrantType);
writer.String(kClientGrantType);

auto expires_in = static_cast<unsigned int>(properties.expires_in.count());
if (expires_in > 0) {
writer.Key(Constants::EXPIRES_IN);
writer.Uint(expires_in);
}

if (properties.scope) {
writer.Key(kScope);
writer.String(properties.scope.get().c_str());
}
writer.EndObject();
auto content = data.GetString();
return std::make_shared<std::vector<unsigned char> >(
Expand Down Expand Up @@ -764,7 +768,6 @@ AuthenticationClient::Impl::generateUserBody(const UserProperties& properties) {
writer.Key(Constants::EXPIRES_IN);
writer.Uint(properties.expires_in);
}

writer.EndObject();
auto content = data.GetString();
return std::make_shared<std::vector<unsigned char> >(
Expand Down Expand Up @@ -842,7 +845,6 @@ AuthenticationClient::Impl::generateRefreshBody(
writer.Key(Constants::EXPIRES_IN);
writer.Uint(properties.expires_in);
}

writer.EndObject();
auto content = data.GetString();
return std::make_shared<std::vector<unsigned char> >(
Expand Down Expand Up @@ -942,7 +944,16 @@ client::CancellationToken AuthenticationClient::SignInClient(
const AuthenticationCredentials& credentials,
const SignInClientCallback& callback,
const std::chrono::seconds& expires_in) {
return impl_->SignInClient(credentials, expires_in, callback);
SignInProperties properties;
properties.expires_in = expires_in;
return impl_->SignInClient(credentials, properties, callback);
}

client::CancellationToken AuthenticationClient::SignInClient(
AuthenticationCredentials credentials, SignInProperties properties,
SignInClientCallback callback) {
return impl_->SignInClient(std::move(credentials), std::move(properties),
std::move(callback));
}

client::CancellationToken AuthenticationClient::SignInHereUser(
Expand Down
2 changes: 2 additions & 0 deletions olp-cpp-sdk-authentication/src/SignInResult.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ const std::string& SignInResult::GetUserIdentifier() const {
return impl_->GetUserIdentifier();
}

const std::string& SignInResult::GetScope() const { return impl_->GetScope(); }

SignInResult::SignInResult(std::shared_ptr<SignInResultImpl> impl)
: impl_(impl) {}

Expand Down
5 changes: 5 additions & 0 deletions olp-cpp-sdk-authentication/src/SignInResultImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
namespace {
constexpr auto kTokenType = "tokenType";
constexpr auto kUserId = "userId";
constexpr auto kScope = "scope";
} // namespace

namespace olp {
Expand Down Expand Up @@ -60,6 +61,8 @@ SignInResultImpl::SignInResultImpl(
(*json_document)[Constants::EXPIRES_IN].GetUint();
if (json_document->HasMember(kUserId))
user_identifier_ = (*json_document)[kUserId].GetString();
if (json_document->HasMember(kScope))
scope_ = (*json_document)[kScope].GetString();
}
}
}
Expand All @@ -84,6 +87,8 @@ const std::string& SignInResultImpl::GetUserIdentifier() const {
return user_identifier_;
}

const std::string& SignInResultImpl::GetScope() const { return scope_; }

bool SignInResultImpl::IsValid() const { return is_valid_; }

} // namespace authentication
Expand Down
3 changes: 3 additions & 0 deletions olp-cpp-sdk-authentication/src/SignInResultImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ class SignInResultImpl : public BaseResult {
*/
bool IsValid() const override;

const std::string& GetScope() const;

private:
bool is_valid_;

Expand All @@ -85,6 +87,7 @@ class SignInResultImpl : public BaseResult {
time_t expiry_time_;
std::string refresh_token_;
std::string user_identifier_;
std::string scope_;
};
} // namespace authentication
} // namespace olp
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ void TestCheckErrorFields(const ErrorFields& errorFields) {

class AuthenticationClientTest : public ::testing::Test {
public:
AuthenticationClientTest() : key_("key"), secret_("secret") {}
AuthenticationClientTest()
: key_("key"), secret_("secret"), scope_("scope") {}
void SetUp() {
client_ = std::make_unique<AuthenticationClient>(
"https://authentication.server.url");
Expand Down Expand Up @@ -209,8 +210,58 @@ class AuthenticationClientTest : public ::testing::Test {
std::shared_ptr<olp::thread::TaskScheduler> task_scheduler_;
const std::string key_;
const std::string secret_;
const std::string scope_;
};

TEST_F(AuthenticationClientTest, SignInClientScope) {
AuthenticationCredentials credentials(key_, secret_);
std::promise<AuthenticationClient::SignInClientResponse> request;
auto request_future = request.get_future();

EXPECT_CALL(*network_, Send(_, _, _, _, _))
.WillOnce([&](olp::http::NetworkRequest request,
olp::http::Network::Payload payload,
olp::http::Network::Callback callback,
olp::http::Network::HeaderCallback header_callback,
olp::http::Network::DataCallback data_callback) {
olp::http::RequestId request_id(5);
if (payload) {
*payload << kResponseWithScope;
}
callback(olp::http::NetworkResponse()
.WithRequestId(request_id)
.WithStatus(olp::http::HttpStatusCode::OK));
if (data_callback) {
auto raw = const_cast<char*>(kResponseWithScope.c_str());
data_callback(reinterpret_cast<uint8_t*>(raw), 0,
kResponseWithScope.size());
}

return olp::http::SendOutcome(request_id);
});

AuthenticationClient::SignInProperties properties;
properties.scope = scope_;
std::time_t now = std::time(nullptr);
client_->SignInClient(
credentials, properties,
[&](const AuthenticationClient::SignInClientResponse& response) {
request.set_value(response);
});
request_future.wait();

AuthenticationClient::SignInClientResponse response = request_future.get();
EXPECT_TRUE(response.IsSuccessful());
EXPECT_FALSE(response.GetResult().GetAccessToken().empty());
EXPECT_EQ(kResponseToken, response.GetResult().GetAccessToken());
EXPECT_GE(now + kMaxExpiryTime, response.GetResult().GetExpiryTime());
EXPECT_LT(now + kMinExpiryTime, response.GetResult().GetExpiryTime());
EXPECT_EQ("bearer", response.GetResult().GetTokenType());
EXPECT_TRUE(response.GetResult().GetRefreshToken().empty());
EXPECT_TRUE(response.GetResult().GetUserIdentifier().empty());
EXPECT_EQ(response.GetResult().GetScope(), scope_);
}

TEST_F(AuthenticationClientTest, SignInClientData) {
AuthenticationCredentials credentials("key_", secret_);
std::promise<AuthenticationClient::SignInClientResponse> request;
Expand Down Expand Up @@ -263,32 +314,7 @@ TEST_F(AuthenticationClientTest, SignInClientData) {
AuthenticationClient::SignInClientResponse response = request_future.get();
EXPECT_TRUE(response.IsSuccessful());
EXPECT_FALSE(response.GetResult().GetAccessToken().empty());
EXPECT_EQ(
"tyJhbGciOiJSUzUxMiIsImN0eSI6IkpXVCIsImlzcyI6IkhFUkUiLCJhaWQiOiJTcFR5dkQ0"
"RjZ1dWhVY0t3Zj"
"BPRC"
"IsImlhdCI6MTUyMjY5OTY2MywiZXhwIjoxNTIyNzAzMjYzLCJraWQiOiJqMSJ9."
"ZXlKaGJHY2lPaUprYVhJaUxDSmxibU1pT2lKQk1qVTJRMEpETFVoVE5URXlJbjAuLkNuSXBW"
"VG14bFBUTFhqdF"
"l0OD"
"VodVEuTk1aMzRVSndtVnNOX21Zd3pwa1UydVFfMklCbE9QeWw0VEJWQnZXczcwRXdoQWRld0"
"tpR09KOGFHOWtK"
"eTBo"
"YWg2SS03Y01WbXQ4S3ppUHVKOXZqV2U1Q0F4cER0LU0yQUxhQTJnZWlIZXJuaEEwZ1ZRR3pV"
"akw5OEhDdkpEc2"
"YuQX"
"hxNTRPTG9FVDhqV2ZreTgtZHY4ZUR1SzctRnJOWklGSms0RHZGa2F5Yw.bfSc5sXovW0-"
"yGTqWDZtsVvqIxeNl9IGFbtzRBRkHCHEjthZzeRscB6oc707JTpiuRmDKJe6oFU03RocTS99"
"YBlM3p5rP2moad"
"DNmP"
"3Uag4elo6z0ZE_w1BP7So7rMX1k4NymfEATdmyXVnjAhBlTPQqOYIWV-"
"UNCXWCIzLSuwaJ96N1d8XZeiA1jkpsp4CKfcSSm9hgsKNA95SWPnZAHyqOYlO0sDE28osOIj"
"N2UVSUKlO1BDtL"
"iPLt"
"a_dIqvqFUU5aRi_"
"dcYqkJcZh195ojzeAcvDGI6HqS2zUMTdpYUhlwwfpkxGwrFmlAxgx58xKSeVt0sPvtabZBAW"
"8uh2NGg",
response.GetResult().GetAccessToken());
EXPECT_EQ(kResponseToken, response.GetResult().GetAccessToken());
EXPECT_GE(now + kMaxExpiryTime, response.GetResult().GetExpiryTime());
EXPECT_LT(now + kMinExpiryTime, response.GetResult().GetExpiryTime());
EXPECT_EQ("bearer", response.GetResult().GetTokenType());
Expand All @@ -308,32 +334,7 @@ TEST_F(AuthenticationClientTest, SignInClientData) {
AuthenticationClient::SignInClientResponse response_2 =
request_future_2.get();
EXPECT_TRUE(response_2.IsSuccessful());
EXPECT_EQ(
"tyJhbGciOiJSUzUxMiIsImN0eSI6IkpXVCIsImlzcyI6IkhFUkUiLCJhaWQiOiJTcFR5dkQ0"
"RjZ1dWhVY0t3Zj"
"BPRC"
"IsImlhdCI6MTUyMjY5OTY2MywiZXhwIjoxNTIyNzAzMjYzLCJraWQiOiJqMSJ9."
"ZXlKaGJHY2lPaUprYVhJaUxDSmxibU1pT2lKQk1qVTJRMEpETFVoVE5URXlJbjAuLkNuSXBW"
"VG14bFBUTFhqdF"
"l0OD"
"VodVEuTk1aMzRVSndtVnNOX21Zd3pwa1UydVFfMklCbE9QeWw0VEJWQnZXczcwRXdoQWRld0"
"tpR09KOGFHOWtK"
"eTBo"
"YWg2SS03Y01WbXQ4S3ppUHVKOXZqV2U1Q0F4cER0LU0yQUxhQTJnZWlIZXJuaEEwZ1ZRR3pV"
"akw5OEhDdkpEc2"
"YuQX"
"hxNTRPTG9FVDhqV2ZreTgtZHY4ZUR1SzctRnJOWklGSms0RHZGa2F5Yw.bfSc5sXovW0-"
"yGTqWDZtsVvqIxeNl9IGFbtzRBRkHCHEjthZzeRscB6oc707JTpiuRmDKJe6oFU03RocTS99"
"YBlM3p5rP2moad"
"DNmP"
"3Uag4elo6z0ZE_w1BP7So7rMX1k4NymfEATdmyXVnjAhBlTPQqOYIWV-"
"UNCXWCIzLSuwaJ96N1d8XZeiA1jkpsp4CKfcSSm9hgsKNA95SWPnZAHyqOYlO0sDE28osOIj"
"N2UVSUKlO1BDtL"
"iPLt"
"a_dIqvqFUU5aRi_"
"dcYqkJcZh195ojzeAcvDGI6HqS2zUMTdpYUhlwwfpkxGwrFmlAxgx58xKSeVt0sPvtabZBAW"
"8uh2NGg",
response_2.GetResult().GetAccessToken());
EXPECT_EQ(kResponseToken, response_2.GetResult().GetAccessToken());
;
EXPECT_GE(now_2 + kMaxExpiryTime, response_2.GetResult().GetExpiryTime());
EXPECT_LT(now_2 + kMinExpiryTime, response_2.GetResult().GetExpiryTime());
Expand Down
Loading

0 comments on commit 9790f56

Please sign in to comment.