Skip to content

Commit b4d767c

Browse files
authored
Add proper cancellation for the new token provider (#1269)
The inner class TokenEndpoint::Impl is extructed from TokenEndpoint.cpp to separate files TokenEndpointImpl.h and TokenEndpointImpl.cpp. TokenEndpointImpl is extended with mostly copy-pasted code from AuthenticationClientImpl, which is supposed to be refactored in the future to use TokenProvider, and not in the opposite direction, as it is now. TokenEndpointImpl code is adapted in a way to give an abilty to pass cancellation context from the new token profider to OlpClient::CallApi(). A new test class TokenEndpointTest contains adapted tests copy-pasted from AuthenticationClientTest. Integration tests for cancellation and timeout are added. Relates-To: OLPEDGE-2651 Signed-off-by: Mykola Malik <[email protected]>
1 parent 397d025 commit b4d767c

File tree

11 files changed

+1490
-117
lines changed

11 files changed

+1490
-117
lines changed

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

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "TokenRequest.h"
2929
#include "TokenResult.h"
3030

31+
#include "olp/core/client/CancellationContext.h"
3132
#include "olp/core/client/CancellationToken.h"
3233
#include "olp/core/porting/warning_disable.h"
3334

@@ -67,8 +68,6 @@ class AUTHENTICATION_API OLP_SDK_DEPRECATED("Will be removed by 10.2020.")
6768
* @note This method is blocked when a new token needs to be retrieved.
6869
* Therefore, the token should not be called from a time-sensitive thread (for
6970
* example, the UI thread).
70-
* @throw std::future_error Thrown when a failure occurs.
71-
* Contains the error code and message.
7271
*
7372
* @param cancellation_token The token used to cancel the operation.
7473
* @param minimum_validity (Optional) Sets the minimum validity period of
@@ -78,11 +77,32 @@ class AUTHENTICATION_API OLP_SDK_DEPRECATED("Will be removed by 10.2020.")
7877
* @return The `TokenResponse` instance if the old one is expired.
7978
* Otherwise, returns the cached `TokenResponse` instance.
8079
*/
81-
8280
TokenResponse GetToken(client::CancellationToken& cancellation_token,
8381
const std::chrono::seconds& minimum_validity =
8482
kDefaultMinimumValiditySeconds) const;
8583

84+
/**
85+
* @brief Synchronously gets a token that is always fresh.
86+
*
87+
* If no token has been retrieved yet or the current token is expired or
88+
* expires within five minutes, a new token is requested. Otherwise,
89+
* the cached token is returned. This method is thread-safe.
90+
* @note This method is blocked when a new token needs to be retrieved.
91+
* Therefore, the token should not be called from a time-sensitive thread (for
92+
* example, the UI thread).
93+
*
94+
* @param context Used to cancel the pending token request.
95+
* @param minimum_validity (Optional) Sets the minimum validity period of
96+
* the token in seconds. The default validity period is 5 minutes. If
97+
* the period is set to 0, the token is refreshed immediately.
98+
*
99+
* @return The `TokenResponse` instance if the old one is expired.
100+
* Otherwise, returns the cached `TokenResponse` instance.
101+
*/
102+
TokenResponse GetToken(client::CancellationContext& context,
103+
const std::chrono::seconds& minimum_validity =
104+
kDefaultMinimumValiditySeconds) const;
105+
86106
/**
87107
* @brief Synchronously gets a token that is always fresh.
88108
*
@@ -92,8 +112,6 @@ class AUTHENTICATION_API OLP_SDK_DEPRECATED("Will be removed by 10.2020.")
92112
* @note This method is blocked when a new token needs to be retrieved.
93113
* Therefore, it should not be called from a time-sensitive thread (for
94114
* example, the UI thread).
95-
* @throw std::future_error Thrown when a failure occurs.
96-
* Contains the error code and message.
97115
*
98116
* @param minimum_validity (Optional) Sets the minimum validity period of
99117
* the token in seconds. The default validity period is 5 minutes. If

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ class AUTHENTICATION_API SignInResult {
134134
private:
135135
friend class SignInUserResult;
136136
friend class AuthenticationClientImpl;
137+
friend class TokenEndpointImpl;
137138

138139
SignInResult(std::shared_ptr<SignInResultImpl> impl);
139140

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

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,14 @@
3030
#include <olp/authentication/TokenResult.h>
3131
#include <olp/authentication/Types.h>
3232
#include <olp/core/client/ApiResponse.h>
33+
#include <olp/core/client/CancellationContext.h>
3334
#include <olp/core/client/CancellationToken.h>
3435
#include <olp/core/porting/warning_disable.h>
3536

3637
namespace olp {
3738
namespace authentication {
3839
class AutoRefreshingToken;
40+
class TokenEndpointImpl;
3941

4042
/**
4143
* @brief Corresponds to the token endpoint as specified in the OAuth2.0
@@ -89,6 +91,22 @@ class AUTHENTICATION_API OLP_SDK_DEPRECATED("Will be removed in 10.2020")
8991
client::CancellationToken& cancellation_token,
9092
const TokenRequest& token_request = TokenRequest()) const;
9193

94+
/**
95+
* @brief Executes the POST request method to the token endpoint.
96+
*
97+
* The request gets the HERE Access token that is used to access the HERE
98+
* platform Services. Returns the token that is used as the `Authorization:
99+
* Bearer` token value.
100+
*
101+
* @param context Used to cancel the pending token request.
102+
* @param token_request The `TokenRequest` instance.
103+
*
104+
* @return The `TokenResponse` instance.
105+
*/
106+
TokenResponse RequestToken(
107+
client::CancellationContext& context,
108+
const TokenRequest& token_request = TokenRequest()) const;
109+
92110
/**
93111
* Executes the POST request method to the token endpoint.
94112
*
@@ -127,8 +145,7 @@ class AUTHENTICATION_API OLP_SDK_DEPRECATED("Will be removed in 10.2020")
127145
explicit TokenEndpoint(Settings settings);
128146

129147
private:
130-
class Impl;
131-
std::shared_ptr<Impl> impl_;
148+
std::shared_ptr<TokenEndpointImpl> impl_;
132149
};
133150

134151
} // namespace authentication

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -173,13 +173,11 @@ class TokenProvider {
173173
/// Gets the token response from `AutoRefreshingToken` or requests a new
174174
/// token if the token response expired or is not present.
175175
TokenResponse GetResponse(client::CancellationContext& context) const {
176-
OLP_SDK_CORE_UNUSED(context);
177-
178176
// Mutex is needed to prevent multiple authorization requests that can
179177
// happen when the token is not available, and multiple consumers
180178
// requested it.
181179
std::lock_guard<std::mutex> lock(request_mutex_);
182-
return token_.GetToken(minimum_validity_);
180+
return token_.GetToken(context, minimum_validity_);
183181
}
184182

185183
/// Checks whether the available token response is valid.

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

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ struct AutoRefreshingToken::Impl {
5959

6060
TokenResponse GetToken(client::CancellationToken& cancellation_token,
6161
std::chrono::seconds minimum_validity) {
62-
if (ForceRefresh(minimum_validity) || ShouldRefreshNow()) {
62+
if (ShouldRefreshNow(minimum_validity)) {
6363
OLP_SDK_LOG_INFO_F(kLogTag, "Time to refresh token");
6464
TryRefreshCurrentToken(cancellation_token, minimum_validity);
6565
}
@@ -68,9 +68,20 @@ struct AutoRefreshingToken::Impl {
6868
return current_token_;
6969
}
7070

71+
TokenResponse GetToken(client::CancellationContext& context,
72+
std::chrono::seconds minimum_validity) {
73+
if (ShouldRefreshNow(minimum_validity)) {
74+
OLP_SDK_LOG_INFO_F(kLogTag, "Time to refresh token");
75+
TryRefreshCurrentToken(context, minimum_validity);
76+
}
77+
78+
// Return existing still valid token
79+
return current_token_;
80+
}
81+
7182
client::CancellationToken GetToken(const GetTokenCallback& callback,
7283
std::chrono::seconds minimum_validity) {
73-
if (ForceRefresh(minimum_validity) || ShouldRefreshNow()) {
84+
if (ShouldRefreshNow(minimum_validity)) {
7485
OLP_SDK_LOG_INFO_F(kLogTag, "Time to refresh token");
7586
return TryRefreshCurrentToken(callback, minimum_validity);
7687
}
@@ -81,12 +92,9 @@ struct AutoRefreshingToken::Impl {
8192
}
8293

8394
private:
84-
bool ShouldRefreshNow() const {
85-
return std::chrono::steady_clock::now() >= token_refresh_time_;
86-
}
87-
88-
bool ForceRefresh(const std::chrono::seconds& minimum_validity) const {
89-
return minimum_validity <= std::chrono::seconds(0);
95+
bool ShouldRefreshNow(const std::chrono::seconds& minimum_validity) const {
96+
return minimum_validity <= std::chrono::seconds(0) ||
97+
std::chrono::steady_clock::now() >= token_refresh_time_;
9098
}
9199

92100
void TryRefreshCurrentToken(client::CancellationToken& cancellation_token,
@@ -110,6 +118,26 @@ struct AutoRefreshingToken::Impl {
110118
token_refresh_time_ = ComputeRefreshTime(current_token_, minimum_validity);
111119
}
112120

121+
void TryRefreshCurrentToken(client::CancellationContext& context,
122+
const std::chrono::seconds& minimum_validity) {
123+
std::lock_guard<std::mutex> guard(token_mutex_);
124+
current_token_ = token_endpoint_.RequestToken(context, token_request_);
125+
126+
if (!current_token_) {
127+
OLP_SDK_LOG_INFO_F(
128+
kLogTag, "Token NOK, error_code=%d, http_status=%d, message='%s'",
129+
static_cast<int>(current_token_.GetError().GetErrorCode()),
130+
current_token_.GetError().GetHttpStatusCode(),
131+
current_token_.GetError().GetMessage().c_str());
132+
} else {
133+
auto expiry_time = current_token_.GetResult().GetExpiryTime();
134+
OLP_SDK_LOG_INFO_F(kLogTag, "Token OK, expires=%s",
135+
std::asctime(std::gmtime(&expiry_time)));
136+
}
137+
138+
token_refresh_time_ = ComputeRefreshTime(current_token_, minimum_validity);
139+
}
140+
113141
client::CancellationToken TryRefreshCurrentToken(
114142
const GetTokenCallback& callback,
115143
const std::chrono::seconds& minimum_validity) {
@@ -162,6 +190,12 @@ TokenResponse AutoRefreshingToken::GetToken(
162190
return impl_->GetToken(cancellation_token, minimum_validity);
163191
}
164192

193+
TokenResponse AutoRefreshingToken::GetToken(
194+
client::CancellationContext& context,
195+
const std::chrono::seconds& minimum_validity) const {
196+
return impl_->GetToken(context, minimum_validity);
197+
}
198+
165199
TokenResponse AutoRefreshingToken::GetToken(
166200
const std::chrono::seconds& minimum_validity) const {
167201
client::CancellationToken cancellation_token;

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

Lines changed: 12 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -17,95 +17,24 @@
1717
* License-Filename: LICENSE
1818
*/
1919

20-
#include "olp/authentication/TokenEndpoint.h"
20+
#include <olp/authentication/TokenEndpoint.h>
2121

2222
PORTING_PUSH_WARNINGS()
2323
PORTING_CLANG_GCC_DISABLE_WARNING("-Wdeprecated-declarations")
2424

25-
#include "olp/authentication/AuthenticationClient.h"
26-
#include "olp/authentication/AuthenticationCredentials.h"
27-
#include "olp/authentication/AutoRefreshingToken.h"
28-
#include "olp/authentication/ErrorResponse.h"
29-
#include "olp/authentication/SignInResult.h"
30-
#include "olp/core/logging/Log.h"
25+
#include <olp/authentication/AuthenticationClient.h>
26+
#include <olp/authentication/AutoRefreshingToken.h>
27+
#include <olp/core/logging/Log.h>
28+
#include "TokenEndpointImpl.h"
3129

3230
namespace olp {
3331
namespace authentication {
3432

3533
namespace {
3634
static const std::string kOauth2TokenEndpoint = "/oauth2/token";
3735
static constexpr auto kLogTag = "TokenEndpoint";
38-
39-
AuthenticationSettings ConvertSettings(Settings settings) {
40-
AuthenticationSettings auth_settings;
41-
auth_settings.network_proxy_settings = settings.network_proxy_settings;
42-
// Ignore task scheduler. It can cause a dealock on the sign in when used from
43-
// another task within `TaskScheduler` with 1 thread.
44-
//auth_settings.task_scheduler = settings.task_scheduler;
45-
auth_settings.network_request_handler = settings.network_request_handler;
46-
auth_settings.token_endpoint_url = settings.token_endpoint_url;
47-
auth_settings.use_system_time = settings.use_system_time;
48-
auth_settings.retry_settings = settings.retry_settings;
49-
return auth_settings;
50-
}
5136
} // namespace
5237

53-
class TokenEndpoint::Impl {
54-
public:
55-
explicit Impl(Settings settings);
56-
57-
client::CancellationToken RequestToken(const TokenRequest& token_request,
58-
const RequestTokenCallback& callback);
59-
60-
std::future<TokenResponse> RequestToken(
61-
client::CancellationToken& cancel_token,
62-
const TokenRequest& token_request);
63-
64-
private:
65-
AuthenticationClient auth_client_;
66-
AuthenticationCredentials auth_credentials_;
67-
};
68-
69-
TokenEndpoint::Impl::Impl(Settings settings)
70-
: auth_client_(ConvertSettings(settings)),
71-
auth_credentials_(std::move(settings.credentials)) {}
72-
73-
client::CancellationToken TokenEndpoint::Impl::RequestToken(
74-
const TokenRequest& token_request, const RequestTokenCallback& callback) {
75-
AuthenticationClient::SignInProperties properties;
76-
properties.expires_in = token_request.GetExpiresIn();
77-
return auth_client_.SignInClient(
78-
auth_credentials_, properties,
79-
[callback](
80-
const AuthenticationClient::SignInClientResponse& sign_in_response) {
81-
if (!sign_in_response) {
82-
callback(sign_in_response.GetError());
83-
return;
84-
}
85-
86-
const auto& sign_in_result = sign_in_response.GetResult();
87-
if (sign_in_result.GetAccessToken().empty()) {
88-
callback(client::ApiError{sign_in_result.GetStatus(),
89-
sign_in_result.GetFullMessage()});
90-
return;
91-
}
92-
93-
callback(TokenResult{sign_in_result.GetAccessToken(),
94-
sign_in_result.GetExpiresIn(),
95-
http::HttpStatusCode::OK, ErrorResponse{}});
96-
});
97-
}
98-
99-
std::future<TokenResponse> TokenEndpoint::Impl::RequestToken(
100-
client::CancellationToken& cancel_token,
101-
const TokenRequest& token_request) {
102-
auto promise = std::make_shared<std::promise<TokenResponse>>();
103-
cancel_token = RequestToken(token_request, [promise](TokenResponse response) {
104-
promise->set_value(std::move(response));
105-
});
106-
return promise->get_future();
107-
}
108-
10938
TokenEndpoint::TokenEndpoint(Settings settings) {
11039
// The underlying auth library expects a base URL and appends /oauth2/token
11140
// endpoint to it. Therefore if /oauth2/token is found it is stripped from the
@@ -121,7 +50,7 @@ TokenEndpoint::TokenEndpoint(Settings settings) {
12150
"standard OAuth2 token endpoint URLs are supported.");
12251
}
12352

124-
impl_ = std::make_shared<TokenEndpoint::Impl>(std::move(settings));
53+
impl_ = std::make_shared<TokenEndpointImpl>(std::move(settings));
12554
}
12655

12756
client::CancellationToken TokenEndpoint::RequestToken(
@@ -136,6 +65,12 @@ std::future<TokenResponse> TokenEndpoint::RequestToken(
13665
return impl_->RequestToken(cancellation_token, token_request);
13766
}
13867

68+
TokenResponse TokenEndpoint::RequestToken(
69+
client::CancellationContext& context,
70+
const TokenRequest& token_request) const {
71+
return impl_->RequestToken(context, token_request);
72+
}
73+
13974
std::future<TokenResponse> TokenEndpoint::RequestToken(
14075
const TokenRequest& token_request) const {
14176
client::CancellationToken cancellation_token;

0 commit comments

Comments
 (0)