-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathauthforge_sdk.h
More file actions
189 lines (169 loc) · 6.99 KB
/
authforge_sdk.h
File metadata and controls
189 lines (169 loc) · 6.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
#pragma once
#include <cstdint>
#include <exception>
#include <functional>
#include <mutex>
#include <optional>
#include <unordered_set>
#include <string>
#include <utility>
#include <vector>
namespace authforge {
struct ValidateLicenseResult {
bool valid = false;
std::string errorCode;
std::string sessionToken;
long long expiresIn = 0;
std::string sessionDataJson;
std::string appVariablesJson;
std::string licenseVariablesJson;
std::string keyId;
std::optional<std::string> sessionExpiresAt;
bool licenseExpirationKnown = false;
/// ISO 8601 when `licenseExpirationKnown` and key is dated; empty when lifetime.
std::string licenseExpiresAt;
std::optional<int> maxHwidSlots;
std::optional<int> hwidCount;
std::optional<std::string> licenseLabel;
};
class AuthForgeClient {
public:
static constexpr const char *kDefaultApiBaseUrl = "https://auth.authforge.cc";
/// Single-key constructor preserved for source compatibility. The provided
/// string may also be a comma-separated trust list (current,previous) so
/// callers can roll a key by re-deploying with an env var change.
AuthForgeClient(
std::string appId,
std::string appSecret,
std::string publicKey,
std::string heartbeatMode,
int heartbeatInterval = 900,
std::string apiBaseUrl = kDefaultApiBaseUrl,
std::function<void(const std::string &, const std::exception *)> onFailure = nullptr,
int requestTimeout = 15,
int ttlSeconds = 0,
std::string hwidOverride = "");
/// Rotation-aware constructor. The first entry should be the *current* key
/// (it is reflected back through GetPublicKey() for diagnostics);
/// subsequent entries are still trusted to support overlap windows.
AuthForgeClient(
std::string appId,
std::string appSecret,
std::vector<std::string> publicKeys,
std::string heartbeatMode,
int heartbeatInterval = 900,
std::string apiBaseUrl = kDefaultApiBaseUrl,
std::function<void(const std::string &, const std::exception *)> onFailure = nullptr,
int requestTimeout = 15,
int ttlSeconds = 0,
std::string hwidOverride = "");
/// Returns the configured trust list. Useful for tests and observability.
const std::vector<std::string> &GetPublicKeys() const noexcept { return publicKeys_; }
bool Login(const std::string &licenseKey);
/// Same cryptographic validation as Login without persisting session state or starting heartbeats.
ValidateLicenseResult ValidateLicense(const std::string &licenseKey);
bool SelfBan(const std::string &licenseKey = "",
const std::string &sessionToken = "",
bool revokeLicense = true,
bool blacklistHwid = true,
bool blacklistIp = true);
void Logout();
bool IsAuthenticated() const;
std::optional<std::string> GetSessionDataJson() const;
std::optional<std::string> GetAppVariablesJson() const;
std::optional<std::string> GetLicenseVariablesJson() const;
private:
struct JsonValue {
bool exists = false;
bool isString = false;
std::string value;
};
enum class SigningContext { Validate, Heartbeat };
void StartHeartbeatOnce();
void HeartbeatLoop() noexcept;
void ServerHeartbeat();
void LocalHeartbeat();
void ValidateAndStore(const std::string &licenseKey);
void ApplySignedResponse(
const std::string &responseJson,
const std::string &expectedNonce,
const std::optional<std::string> &licenseKey,
SigningContext context,
bool persistToSession = true,
ValidateLicenseResult *validateOnlyOut = nullptr);
std::string PostJson(const std::string &path, const std::string &bodyJson, std::string *usedNonce = nullptr) const;
std::string ExtractServerError(const std::string &responseJson) const;
void Fail(const std::string &reason, const std::exception *exc = nullptr) const noexcept;
std::string GetHwid() const;
std::string SafeMacAddress() const;
std::string SafeCpuInfo() const;
std::string SafeDiskSerial() const;
std::string RunCommand(const std::string &command) const;
static bool ExtractJsonValue(const std::string &json, const std::string &key, JsonValue &outValue);
static std::optional<std::string> ExtractJsonString(const std::string &json, const std::string &key);
static std::optional<long long> ExtractJsonInt(const std::string &json, const std::string &key);
static std::string BuildJsonBody(const std::vector<std::pair<std::string, std::string>> &pairs);
static std::string EscapeJsonString(const std::string &value);
static std::string UnescapeJsonString(const std::string &value, bool &ok);
static std::string Trim(const std::string &value);
static std::string ToLower(std::string value);
static std::string GenerateNonceHex32();
static std::vector<unsigned char> Sha256Bytes(const std::string &input);
static std::string Sha256Hex(const std::string &input);
static std::string BytesToHexLower(const std::vector<unsigned char> &bytes);
static std::vector<unsigned char> DecodeBase64Any(const std::string &value);
static std::vector<unsigned char> DecodeBase64WithAlphabet(const std::string &value, bool urlSafe);
static std::string AddBase64Padding(const std::string &value);
static bool IsSuccessStatus(const JsonValue &status);
static std::optional<long long> ExtractExpiresInFromSessionToken(const std::string &sessionToken);
static std::optional<std::string> DecodeSessionTokenBody(const std::string &sessionToken);
void VerifySignature(const std::string &rawPayloadB64, const std::string &signature) const;
static std::vector<std::string> SplitCommaTrustList(const std::string &value);
std::string appId_;
std::string appSecret_;
std::vector<std::string> publicKeys_;
std::string heartbeatMode_;
int heartbeatInterval_;
std::string apiBaseUrl_;
std::function<void(const std::string &, const std::exception *)> onFailure_;
int requestTimeout_;
// Requested session token lifetime in seconds for /auth/validate. 0 means
// "let the server pick its default" (24h today). Server clamps to
// [3600, 604800]; preserved across heartbeat refreshes.
int ttlSeconds_;
mutable std::mutex lock_;
bool heartbeatStarted_;
std::string licenseKey_;
std::string sessionToken_;
std::optional<long long> sessionExpiresIn_;
std::string lastNonce_;
std::string rawPayloadB64_;
std::string signature_;
std::string keyId_;
std::vector<std::vector<unsigned char>> verifyPublicKeysBytes_;
std::string sessionDataJson_;
std::string appVariablesJson_;
std::string licenseVariablesJson_;
bool authenticated_ = false;
bool heartbeatStop_ = false;
std::string hwid_;
std::unordered_set<std::string> knownServerErrors_ = {
"invalid_app",
"invalid_key",
"expired",
"revoked",
"hwid_mismatch",
"no_credits",
"app_burn_cap_reached",
"blocked",
"rate_limited",
"replay_detected",
"app_disabled",
"session_expired",
"revoke_requires_session",
"bad_request",
"malformed_request",
"system_error",
};
};
} // namespace authforge