algotradelib
0.0.0.9000
C++ header-only library for algorithmic trading
Toggle main menu visibility
Loading...
Searching...
No Matches
oauth.hpp
1
#pragma once
2
8
#define ATL_TRANSPORT_LAYER_PRESENT 1
9
10
#include <boost/json.hpp>
11
12
#include <charconv>
13
#include <chrono>
14
#include <cstdint>
15
#include <optional>
16
#include <stdexcept>
17
#include <string>
18
#include <string_view>
19
20
namespace
algotradelib
{
21
22
namespace
json = boost::json;
23
24
namespace
network
{
25
35
template
<
typename
BrokerType>
36
class
ClientCredentials
{
37
public
:
39
static
constexpr
std::string_view
grant_type
=
"client_credentials"
;
40
46
void
from_env
() {
static_cast<
BrokerType*
>
(
this
)->from_env_impl(); }
47
54
void
from_json
(
const
std::string& filename) {
55
static_cast<
BrokerType*
>
(
this
)->from_json_impl(filename);
56
}
57
62
const
std::string&
key
() const noexcept {
return
key_
; }
63
68
const
std::string&
secret
() const noexcept {
return
secret_
; }
69
75
std::string
to_jsonbody
()
const
{
76
json::object value;
77
value[
"grant_type"
] =
grant_type
;
78
value[BrokerType::key_field()] =
key_
;
79
value[BrokerType::secret_field()] =
secret_
;
80
return
json::serialize(value);
81
}
82
87
std::string
to_form_urlencoded_body
()
const
{
88
return
"grant_type=client_credentials&client_id="
+ form_encode(
key_
) +
89
"&client_secret="
+ form_encode(
secret_
);
90
}
91
92
protected
:
94
std::string
key_
;
96
std::string
secret_
;
97
98
private
:
99
// Percent-encodes one form value using the RFC 3986 unreserved character set.
100
//
101
// Encoding reserved characters such as `&`, `=`, `+`, and `%` prevents a
102
// credential value from changing the surrounding form-field structure.
103
static
std::string form_encode(std::string_view value) {
104
static
constexpr
char
hex[] =
"0123456789ABCDEF"
;
105
std::string result;
106
result.reserve(value.size());
107
for
(
const
unsigned
char
ch : value) {
108
const
bool
unreserved = (ch >=
'A'
&& ch <=
'Z'
) || (ch >=
'a'
&& ch <=
'z'
) ||
109
(ch >=
'0'
&& ch <=
'9'
) || ch ==
'-'
|| ch ==
'.'
|| ch ==
'_'
|| ch ==
'~'
;
110
if
(unreserved) {
111
result.push_back(
static_cast<
char
>
(ch));
112
}
else
{
113
result.push_back(
'%'
);
114
result.push_back(hex[ch >> 4]);
115
result.push_back(hex[ch & 0x0F]);
116
}
117
}
118
return
result;
119
}
120
};
121
130
template
<
typename
BrokerType>
131
struct
AuthBody
{
133
std::string
access_token_
;
135
std::chrono::system_clock::time_point
expires_at_
{};
136
147
void
from_json
(
const
json::object& document) {
148
const
std::string_view field = BrokerType::token_field();
149
const
auto
token = document.if_contains(field);
150
if
(!token || !token->is_string()) {
151
throw
std::runtime_error(
"token response has no "
+ std::string(field) +
" string"
);
152
}
153
access_token_
= token->as_string().c_str();
154
155
expires_at_
= std::chrono::system_clock::time_point{};
156
if
(
const
auto
value = document.if_contains(
"expires_in"
)) {
157
std::optional<std::int64_t> expires_in;
158
if
(value->is_int64()) {
159
expires_in = value->as_int64();
160
}
else
if
(value->is_uint64()) {
161
expires_in =
static_cast<
std::int64_t
>
(value->as_uint64());
162
}
else
if
(value->is_string()) {
163
expires_in = parse_seconds(value->as_string());
164
}
165
if
(expires_in) {
166
expires_at_
= std::chrono::system_clock::now() + std::chrono::seconds(*expires_in);
167
}
168
}
169
static_cast<
BrokerType*
>
(
this
)->from_json_impl(document);
170
if
(
expires_at_
== std::chrono::system_clock::time_point{}) {
171
throw
std::runtime_error(
172
"token response states no expiry, neither expires_in nor a broker-specific absolute one"
173
);
174
}
175
}
176
182
bool
valid
(std::chrono::seconds margin = std::chrono::minutes(5)) const noexcept {
183
return
!
access_token_
.empty() && std::chrono::system_clock::now() + margin <
expires_at_
;
184
}
185
186
private
:
187
// Reads a whole number of seconds, or nothing when the text is not one. A malformed `expires_in`
188
// leaves `expires_at_` unset, so it reaches the no-expiry refusal rather than throwing an
189
// exception that names neither the broker nor the field.
190
static
std::optional<std::int64_t> parse_seconds(std::string_view text) {
191
std::int64_t seconds{0};
192
const
auto
parsed = std::from_chars(text.data(), text.data() + text.size(), seconds);
193
if
(parsed.ec != std::errc{} || parsed.ptr != text.data() + text.size()) {
194
return
std::nullopt;
195
}
196
return
seconds;
197
}
198
};
199
200
}
// namespace network
201
}
// namespace algotradelib
algotradelib::network::ClientCredentials
OAuth client credentials (an app key and secret), the base of each broker's credentials class.
Definition
oauth.hpp:36
algotradelib::network::ClientCredentials::from_env
void from_env()
Loads the credentials from the broker's environment variables.
Definition
oauth.hpp:46
algotradelib::network::ClientCredentials::to_form_urlencoded_body
std::string to_form_urlencoded_body() const
Builds a form-encoded token-request body (grant_type, client_id, client_secret).
Definition
oauth.hpp:87
algotradelib::network::ClientCredentials::key
const std::string & key() const noexcept
Returns the app key (client id).
Definition
oauth.hpp:62
algotradelib::network::ClientCredentials::grant_type
static constexpr std::string_view grant_type
The OAuth grant type sent in a token request.
Definition
oauth.hpp:39
algotradelib::network::ClientCredentials::key_
std::string key_
The app key (client id).
Definition
oauth.hpp:94
algotradelib::network::ClientCredentials::secret_
std::string secret_
The app secret.
Definition
oauth.hpp:96
algotradelib::network::ClientCredentials::from_json
void from_json(const std::string &filename)
Loads the credentials from a JSON file, in the broker's format.
Definition
oauth.hpp:54
algotradelib::network::ClientCredentials::to_jsonbody
std::string to_jsonbody() const
Builds a JSON token-request body (grant type, key and secret under the broker's field names).
Definition
oauth.hpp:75
algotradelib::network::ClientCredentials::secret
const std::string & secret() const noexcept
Returns the app secret.
Definition
oauth.hpp:68
algotradelib::network
HTTP and WebSocket transport, OAuth tokens, and request pacing.
Definition
http_client.hpp:45
algotradelib
The library: broker-neutral types (core), and the modules below.
Definition
conversion.hpp:13
algotradelib::network::AuthBody
An OAuth access token and its expiry, the base of each broker's token-response class.
Definition
oauth.hpp:131
algotradelib::network::AuthBody::valid
bool valid(std::chrono::seconds margin=std::chrono::minutes(5)) const noexcept
Tests whether the token is set and stays valid for at least a margin.
Definition
oauth.hpp:182
algotradelib::network::AuthBody::expires_at_
std::chrono::system_clock::time_point expires_at_
When the token expires.
Definition
oauth.hpp:135
algotradelib::network::AuthBody::from_json
void from_json(const json::object &document)
Reads a token response.
Definition
oauth.hpp:147
algotradelib::network::AuthBody::access_token_
std::string access_token_
The bearer token; do not log it.
Definition
oauth.hpp:133
algotradelib
src
network
oauth.hpp
Generated by
1.18.0