Skip to content

Commit 9da0a72

Browse files
Add timestamp query in AuthenticationClient::SignInClient (#578)
* Add timestamp query in AuthenticationClient::SignInClient * Add additional timestamp https request while using SignInClient method * Change token expiriation checking to use steady clock instead of system clock. * Add new public API to TokenResult and SignInResult to get expiriation time of token in seconds. Resolves: OLPEDGE-837 Signed-off-by: Serhii Lozynskyi <[email protected]>
1 parent 3ca6dc1 commit 9da0a72

File tree

12 files changed

+340
-104
lines changed

12 files changed

+340
-104
lines changed

olp-cpp-sdk-authentication/include/olp/authentication/SignInResult.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#pragma once
2121

22+
#include <chrono>
2223
#include <ctime>
2324
#include <memory>
2425
#include <string>
@@ -109,6 +110,13 @@ class AUTHENTICATION_API SignInResult {
109110
*/
110111
time_t GetExpiryTime() const;
111112

113+
/**
114+
* @brief Gets the access token expiry time in seconds.
115+
*
116+
* @return Duration for which token stays valid.
117+
*/
118+
std::chrono::seconds GetExpiresIn() const;
119+
112120
/**
113121
* @brief Gets the HERE Account user identifier.
114122
*

olp-cpp-sdk-authentication/include/olp/authentication/TokenResult.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919

2020
#pragma once
2121

22+
#include <olp/core/porting/deprecated.h>
23+
24+
#include <chrono>
2225
#include <ctime>
2326
#include <string>
2427

@@ -49,6 +52,16 @@ class AUTHENTICATION_API TokenResult {
4952
TokenResult(std::string access_token, time_t expiry_time, int http_status,
5053
ErrorResponse error);
5154

55+
/**
56+
* @brief Creates the `TokenResult` instance.
57+
*
58+
* @param access_token The access token issued by the authorization server.
59+
* @param expires_in Duration for which token stays valid.
60+
* @param http_status The status code of the HTTP response.
61+
* @param error The error description of the request.
62+
*/
63+
TokenResult(std::string access_token, std::chrono::seconds expires_in,
64+
int http_status, ErrorResponse error);
5265
/**
5366
* @brief Creates the default `TokenResult` instance.
5467
*/
@@ -79,6 +92,13 @@ class AUTHENTICATION_API TokenResult {
7992
* invalid.
8093
*/
8194
time_t GetExpiryTime() const;
95+
96+
/**
97+
* @brief Gets the access token expiry time in seconds.
98+
*
99+
* @return Duration for which token stays valid.
100+
*/
101+
std::chrono::seconds GetExpiresIn() const;
82102

83103
/**
84104
* @brief Gets the HTTP status code.
@@ -99,6 +119,7 @@ class AUTHENTICATION_API TokenResult {
99119
private:
100120
std::string access_token_;
101121
time_t expiry_time_;
122+
std::chrono::seconds expires_in_;
102123
int http_status_;
103124
ErrorResponse error_;
104125
};

olp-cpp-sdk-authentication/src/AuthenticationClient.cpp

Lines changed: 164 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,14 @@
2929
#include <boost/uuid/uuid.hpp>
3030
#include <boost/uuid/uuid_generators.hpp>
3131
#include <boost/uuid/uuid_io.hpp>
32-
3332
#include "Constants.h"
3433
#include "Crypto.h"
3534
#include "SignInResultImpl.h"
3635
#include "SignInUserResultImpl.h"
3736
#include "SignOutResultImpl.h"
3837
#include "SignUpResultImpl.h"
3938
#include "olp/authentication/AuthenticationError.h"
39+
#include "olp/core/client/ApiError.h"
4040
#include "olp/core/client/CancellationToken.h"
4141
#include "olp/core/client/ErrorCode.h"
4242
#include "olp/core/http/HttpStatusCode.h"
@@ -75,6 +75,7 @@ const std::string kOauthEndpoint = "/oauth2/token";
7575
const std::string kSignoutEndpoint = "/logout";
7676
const std::string kTermsEndpoint = "/terms";
7777
const std::string kUserEndpoint = "/user";
78+
const std::string kTimestampEndpoint = "/timestamp";
7879

7980
// JSON fields
8081
constexpr auto kCountryCode = "countryCode";
@@ -179,10 +180,17 @@ class AuthenticationClient::Impl final {
179180
void SetTaskScheduler(std::shared_ptr<TaskScheduler> task_scheduler);
180181

181182
private:
183+
using TimeResponse = client::ApiResponse<time_t, client::ApiError>;
184+
using TimeCallback = std::function<void(TimeResponse)>;
185+
186+
client::CancellationToken GetTimeFromServer(TimeCallback callback);
187+
static TimeResponse ParseTimeResponse(std::stringstream& payload);
188+
182189
std::string base64Encode(const std::vector<uint8_t>& vector);
183190

184191
std::string generateHeader(const AuthenticationCredentials& credentials,
185-
const std::string& url);
192+
const std::string& url,
193+
const time_t& timestamp = std::time(nullptr));
186194
std::string generateBearerHeader(const std::string& bearer_token);
187195

188196
http::NetworkRequest::RequestBodyType generateClientBody(
@@ -264,97 +272,173 @@ client::CancellationToken AuthenticationClient::Impl::SignInClient(
264272
return client::CancellationToken();
265273
}
266274
std::weak_ptr<http::Network> weak_network(network_);
267-
std::string url = server_url_;
268-
url.append(kOauthEndpoint);
269-
http::NetworkRequest request(url);
270-
request.WithVerb(http::NetworkRequest::HttpVerb::POST);
271-
request.WithHeader(http::kAuthorizationHeader,
272-
generateHeader(credentials, url));
273-
request.WithHeader(http::kContentTypeHeader, kApplicationJson);
274-
request.WithHeader(http::kUserAgentHeader, http::kOlpSdkUserAgent);
275-
request.WithSettings(network_settings_);
276275

277-
std::shared_ptr<std::stringstream> payload =
278-
std::make_shared<std::stringstream>();
279-
request.WithBody(generateClientBody(properties));
280276
auto cache = client_token_cache_;
281-
auto send_outcome = network_->Send(
282-
request, payload,
283-
[callback, payload, credentials,
284-
cache](const http::NetworkResponse& network_response) {
285-
auto response_status = network_response.GetStatus();
286-
auto error_msg = network_response.GetError();
287-
288-
// Network not available, use cached token if available
289-
if (response_status < 0) {
290-
// Request cancelled, return
291-
if (response_status == static_cast<int>(olp::http::ErrorCode::CANCELLED_ERROR)) {
292-
callback({{response_status, error_msg}});
293-
return;
294-
}
295277

296-
auto cached_response_found =
297-
cache->locked([credentials, callback](
298-
utils::LruCache<std::string, SignInResult>& c) {
299-
auto it = c.Find(credentials.GetKey());
300-
if (it != c.end()) {
301-
SignInClientResponse response(it->value());
302-
callback(response);
303-
return true;
304-
}
305-
return false;
306-
});
278+
client::CancellationContext context;
307279

308-
if (!cached_response_found) {
309-
// Return an error response
310-
SignInClientResponse error_response(
311-
AuthenticationError(response_status, error_msg));
312-
callback(error_response);
313-
}
280+
auto time_callback = [=](TimeResponse response) mutable {
281+
if (!response.IsSuccessful()) {
282+
callback(AuthenticationError(
283+
static_cast<int>(response.GetError().GetErrorCode()),
284+
response.GetError().GetMessage()));
285+
return;
286+
}
314287

288+
std::string url = server_url_;
289+
url.append(kOauthEndpoint);
290+
http::NetworkRequest request(url);
291+
request.WithVerb(http::NetworkRequest::HttpVerb::POST);
292+
request.WithHeader(http::kAuthorizationHeader,
293+
generateHeader(credentials, url, response.GetResult()));
294+
request.WithHeader(http::kContentTypeHeader, kApplicationJson);
295+
request.WithHeader(http::kUserAgentHeader, http::kOlpSdkUserAgent);
296+
request.WithSettings(network_settings_);
297+
298+
std::shared_ptr<std::stringstream> payload =
299+
std::make_shared<std::stringstream>();
300+
request.WithBody(generateClientBody(properties));
301+
302+
auto network_callback = [callback, payload, credentials, cache](
303+
const http::NetworkResponse& network_response) {
304+
auto response_status = network_response.GetStatus();
305+
auto error_msg = network_response.GetError();
306+
307+
// Network not available, use cached token if available
308+
if (response_status < 0) {
309+
// Request cancelled, return
310+
if (response_status ==
311+
static_cast<int>(olp::http::ErrorCode::CANCELLED_ERROR)) {
312+
callback({{response_status, error_msg}});
315313
return;
316314
}
317315

318-
auto document = std::make_shared<rapidjson::Document>();
319-
rapidjson::IStreamWrapper stream(*payload);
320-
document->ParseStream(stream);
321-
322-
std::shared_ptr<SignInResultImpl> resp_impl =
323-
std::make_shared<SignInResultImpl>(response_status, error_msg,
324-
document);
325-
SignInResult response(resp_impl);
316+
auto cached_response_found =
317+
cache->locked([credentials, callback](
318+
utils::LruCache<std::string, SignInResult>& c) {
319+
auto it = c.Find(credentials.GetKey());
320+
if (it != c.end()) {
321+
SignInClientResponse response(it->value());
322+
callback(response);
323+
return true;
324+
}
325+
return false;
326+
});
326327

327-
if (!resp_impl->IsValid()) {
328-
callback(response);
329-
return;
328+
if (!cached_response_found) {
329+
// Return an error response
330+
SignInClientResponse error_response(
331+
AuthenticationError(response_status, error_msg));
332+
callback(error_response);
330333
}
331334

332-
switch (response_status) {
333-
case http::HttpStatusCode::OK: {
334-
// Cache the response
335-
cache->locked([credentials, &response](
336-
utils::LruCache<std::string, SignInResult>& c) {
337-
return c.InsertOrAssign(credentials.GetKey(), response);
338-
});
339-
// intentially do not break
335+
return;
336+
}
337+
338+
auto document = std::make_shared<rapidjson::Document>();
339+
rapidjson::IStreamWrapper stream(*payload);
340+
document->ParseStream(stream);
341+
342+
std::shared_ptr<SignInResultImpl> resp_impl =
343+
std::make_shared<SignInResultImpl>(response_status, error_msg,
344+
document);
345+
SignInResult response(resp_impl);
346+
347+
if (response_status == http::HttpStatusCode::OK) {
348+
// Cache the response
349+
cache->locked([credentials, &response](
350+
utils::LruCache<std::string, SignInResult>& c) {
351+
return c.InsertOrAssign(credentials.GetKey(), response);
352+
});
353+
}
354+
callback(response);
355+
};
356+
357+
context.ExecuteOrCancelled(
358+
[&]() {
359+
auto send_outcome =
360+
network_->Send(request, payload, network_callback);
361+
if (!send_outcome.IsSuccessful()) {
362+
std::string error_message =
363+
ErrorCodeToString(send_outcome.GetErrorCode());
364+
callback(AuthenticationError(
365+
static_cast<int>(send_outcome.GetErrorCode()), error_message));
366+
return client::CancellationToken();
340367
}
341-
default:
342-
callback(response);
343-
break;
368+
auto request_id = send_outcome.GetRequestId();
369+
return client::CancellationToken([weak_network, request_id]() {
370+
auto network = weak_network.lock();
371+
372+
if (network) {
373+
network->Cancel(request_id);
374+
}
375+
});
376+
},
377+
[&]() {
378+
callback(AuthenticationError(
379+
static_cast<int>(http::ErrorCode::CANCELLED_ERROR), "Cancelled"));
380+
});
381+
};
382+
383+
context.ExecuteOrCancelled(
384+
[&]() { return GetTimeFromServer(std::move(time_callback)); }, []() {});
385+
return client::CancellationToken(
386+
[context]() mutable { context.CancelOperation(); });
387+
}
388+
389+
AuthenticationClient::Impl::TimeResponse
390+
AuthenticationClient::Impl::ParseTimeResponse(std::stringstream& payload) {
391+
auto document = std::make_shared<rapidjson::Document>();
392+
rapidjson::IStreamWrapper stream(payload);
393+
document->ParseStream(stream);
394+
395+
if (!document->IsObject()) {
396+
return client::ApiError(client::ErrorCode::InternalFailure,
397+
"JSON document root is not an Object type");
398+
}
399+
400+
const auto timestamp_it = document->FindMember("timestamp");
401+
if (timestamp_it == document->MemberEnd() || !timestamp_it->value.IsUint()) {
402+
return client::ApiError(
403+
client::ErrorCode::InternalFailure,
404+
"JSON document must contain timestamp integer field");
405+
}
406+
407+
return timestamp_it->value.GetUint();
408+
}
409+
410+
client::CancellationToken AuthenticationClient::Impl::GetTimeFromServer(
411+
TimeCallback callback) {
412+
std::weak_ptr<http::Network> weak_network(network_);
413+
std::string url = server_url_;
414+
url.append(kTimestampEndpoint);
415+
http::NetworkRequest request(url);
416+
request.WithVerb(http::NetworkRequest::HttpVerb::GET);
417+
request.WithHeader(http::kUserAgentHeader, http::kOlpSdkUserAgent);
418+
request.WithSettings(network_settings_);
419+
420+
std::shared_ptr<std::stringstream> payload =
421+
std::make_shared<std::stringstream>();
422+
auto cache = user_token_cache_;
423+
424+
auto send_outcome = network_->Send(
425+
request, payload,
426+
[callback, payload](const http::NetworkResponse& network_response) {
427+
if (network_response.GetStatus() != http::HttpStatusCode::OK) {
428+
callback(
429+
client::ApiError(network_response.GetStatus()));
430+
return;
344431
}
432+
433+
callback(ParseTimeResponse(*payload));
345434
});
346435

347436
if (!send_outcome.IsSuccessful()) {
348-
ExecuteOrSchedule(task_scheduler_, [send_outcome, callback] {
349-
std::string error_message =
350-
ErrorCodeToString(send_outcome.GetErrorCode());
351-
AuthenticationError result({static_cast<int>(send_outcome.GetErrorCode()),
352-
std::move(error_message)});
353-
callback(result);
354-
});
437+
std::string error_message = ErrorCodeToString(send_outcome.GetErrorCode());
438+
callback(client::ApiError(static_cast<int>(send_outcome.GetErrorCode()),
439+
error_message));
355440
return client::CancellationToken();
356441
}
357-
358442
auto request_id = send_outcome.GetRequestId();
359443
return client::CancellationToken([weak_network, request_id]() {
360444
auto network = weak_network.lock();
@@ -432,7 +516,8 @@ client::CancellationToken AuthenticationClient::Impl::HandleUserRequest(
432516
// Network not available, use cached token if available
433517
if (response_status < 0) {
434518
// Request cancelled, return
435-
if (response_status == static_cast<int>(olp::http::ErrorCode::CANCELLED_ERROR)) {
519+
if (response_status ==
520+
static_cast<int>(olp::http::ErrorCode::CANCELLED_ERROR)) {
436521
callback({{response_status, error_msg}});
437522
return;
438523
}
@@ -660,9 +745,10 @@ std::string AuthenticationClient::Impl::base64Encode(
660745
}
661746

662747
std::string AuthenticationClient::Impl::generateHeader(
663-
const AuthenticationCredentials& credentials, const std::string& url) {
748+
const AuthenticationCredentials& credentials, const std::string& url,
749+
const time_t& timestamp) {
664750
std::string uid = generateUid();
665-
const std::string currentTime = std::to_string(std::time(nullptr));
751+
const std::string currentTime = std::to_string(timestamp);
666752
const std::string encodedUri = Url::Encode(url);
667753
const std::string encodedQuery = Url::Encode(
668754
kOauthConsumerKey + kParamEquals + credentials.GetKey() + kParamAdd +

0 commit comments

Comments
 (0)